Index: inc/vcl/bitmapex.hxx =================================================================== --- inc/vcl/bitmapex.hxx (revision 1502181) +++ inc/vcl/bitmapex.hxx (working copy) @@ -393,25 +393,42 @@ @param rTransformation The back transformation for each pixel in (0 .. fWidth),(0 .. fHeight) to local pixel coordiantes + + @param bSmooth + Defines if pixel interpolation is to be used to create the result */ BitmapEx TransformBitmapEx( double fWidth, double fHeight, - const basegfx::B2DHomMatrix& rTransformation) const; + const basegfx::B2DHomMatrix& rTransformation, + bool bSmooth = true) const; /** Create transformed Bitmap @param rTransformation - The transformation from unit coordinates to target + The transformation from unit coordinates to the unit range + @param rVisibleRange + The relative visible range in unit coordinates, relative to (0,0,1,1) which + defines the whole target area + @param fMaximumArea A limitation for the maximum size of pixels to use for the result + @param bSmooth + Defines if pixel interpolation is to be used to create the result + + The traget size of the result bitmap is defined by transforming the given + rTargetRange with the given rTransformation; the area of the result is + linearly scaled to not exceed the given fMaximumArea + @return The transformed bitmap */ BitmapEx getTransformed( const basegfx::B2DHomMatrix& rTransformation, - double fMaximumArea = 500000.0) const; + const basegfx::B2DRange& rVisibleRange, + double fMaximumArea = 500000.0, + bool bSmooth = true) const; /** Create ColorStack-modified version of this BitmapEx Index: source/gdi/bitmapex.cxx =================================================================== --- source/gdi/bitmapex.cxx (revision 1502181) +++ source/gdi/bitmapex.cxx (working copy) @@ -837,180 +837,180 @@ namespace { - void impSmoothPoint(BitmapColor& rValue, const basegfx::B2DPoint& rSource, sal_Int32 nIntX, sal_Int32 nIntY, BitmapReadAccess& rRead) - { - double fDeltaX(rSource.getX() - nIntX); - double fDeltaY(rSource.getY() - nIntY); - sal_Int32 nIndX(0L); - sal_Int32 nIndY(0L); + void impSmoothPoint( + BitmapColor& rValue, + const basegfx::B2DPoint& rSource, + sal_Int32 nIntX, + sal_Int32 nIntY, + BitmapReadAccess& rRead) + { + double fDeltaX(rSource.getX() - (nIntX + 0.5)); + double fDeltaY(rSource.getY() - (nIntY + 0.5)); + sal_Int32 nIndX(0L); + sal_Int32 nIndY(0L); - if(fDeltaX > 0.0 && nIntX + 1L < rRead.Width()) - { - nIndX++; - } - else if(fDeltaX < 0.0 && nIntX >= 1L) - { - fDeltaX = -fDeltaX; - nIndX--; - } - - if(fDeltaY > 0.0 && nIntY + 1L < rRead.Height()) - { - nIndY++; - } - else if(fDeltaY < 0.0 && nIntY >= 1L) - { - fDeltaY = -fDeltaY; - nIndY--; - } + if(fDeltaX > 0.0 && nIntX + 1L < rRead.Width()) + { + nIndX++; + } + else if(fDeltaX < 0.0 && nIntX >= 1L) + { + fDeltaX = -fDeltaX; + nIndX--; + } + + if(fDeltaY > 0.0 && nIntY + 1L < rRead.Height()) + { + nIndY++; + } + else if(fDeltaY < 0.0 && nIntY >= 1L) + { + fDeltaY = -fDeltaY; + nIndY--; + } - if(nIndX || nIndY) - { - const double fColorToReal(1.0 / 255.0); - double fR(rValue.GetRed() * fColorToReal); - double fG(rValue.GetGreen() * fColorToReal); - double fB(rValue.GetBlue() * fColorToReal); - double fRBottom(0.0), fGBottom(0.0), fBBottom(0.0); + if(nIndX || nIndY) + { + const double fColorToReal(1.0 / 255.0); + double fR(rValue.GetRed() * fColorToReal); + double fG(rValue.GetGreen() * fColorToReal); + double fB(rValue.GetBlue() * fColorToReal); + double fRBottom(0.0), fGBottom(0.0), fBBottom(0.0); - if(nIndX) - { - const double fMulA(fDeltaX * fColorToReal); - double fMulB(1.0 - fDeltaX); - const BitmapColor aTopPartner(rRead.GetColor(nIntY, nIntX + nIndX)); + if(nIndX) + { + const double fMulA(fDeltaX * fColorToReal); + double fMulB(1.0 - fDeltaX); + const BitmapColor aTopPartner(rRead.GetColor(nIntY, nIntX + nIndX)); - fR = (fR * fMulB) + (aTopPartner.GetRed() * fMulA); - fG = (fG * fMulB) + (aTopPartner.GetGreen() * fMulA); - fB = (fB * fMulB) + (aTopPartner.GetBlue() * fMulA); + fR = (fR * fMulB) + (aTopPartner.GetRed() * fMulA); + fG = (fG * fMulB) + (aTopPartner.GetGreen() * fMulA); + fB = (fB * fMulB) + (aTopPartner.GetBlue() * fMulA); - if(nIndY) - { - fMulB *= fColorToReal; - const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); - const BitmapColor aBottomPartner(rRead.GetColor(nIntY + nIndY, nIntX + nIndX)); + if(nIndY) + { + fMulB *= fColorToReal; + const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); + const BitmapColor aBottomPartner(rRead.GetColor(nIntY + nIndY, nIntX + nIndX)); - fRBottom = (aBottom.GetRed() * fMulB) + (aBottomPartner.GetRed() * fMulA); - fGBottom = (aBottom.GetGreen() * fMulB) + (aBottomPartner.GetGreen() * fMulA); - fBBottom = (aBottom.GetBlue() * fMulB) + (aBottomPartner.GetBlue() * fMulA); - } - } + fRBottom = (aBottom.GetRed() * fMulB) + (aBottomPartner.GetRed() * fMulA); + fGBottom = (aBottom.GetGreen() * fMulB) + (aBottomPartner.GetGreen() * fMulA); + fBBottom = (aBottom.GetBlue() * fMulB) + (aBottomPartner.GetBlue() * fMulA); + } + } - if(nIndY) - { - if(!nIndX) - { - const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); - - fRBottom = aBottom.GetRed() * fColorToReal; - fGBottom = aBottom.GetGreen() * fColorToReal; - fBBottom = aBottom.GetBlue() * fColorToReal; - } + if(nIndY) + { + if(!nIndX) + { + const BitmapColor aBottom(rRead.GetColor(nIntY + nIndY, nIntX)); + + fRBottom = aBottom.GetRed() * fColorToReal; + fGBottom = aBottom.GetGreen() * fColorToReal; + fBBottom = aBottom.GetBlue() * fColorToReal; + } - const double fMulB(1.0 - fDeltaY); - - fR = (fR * fMulB) + (fRBottom * fDeltaY); - fG = (fG * fMulB) + (fGBottom * fDeltaY); - fB = (fB * fMulB) + (fBBottom * fDeltaY); - } + const double fMulB(1.0 - fDeltaY); + + fR = (fR * fMulB) + (fRBottom * fDeltaY); + fG = (fG * fMulB) + (fGBottom * fDeltaY); + fB = (fB * fMulB) + (fBBottom * fDeltaY); + } - rValue.SetRed((sal_uInt8)(fR * 255.0)); - rValue.SetGreen((sal_uInt8)(fG * 255.0)); - rValue.SetBlue((sal_uInt8)(fB * 255.0)); - } - } + rValue.SetRed((sal_uInt8)(fR * 255.0)); + rValue.SetGreen((sal_uInt8)(fG * 255.0)); + rValue.SetBlue((sal_uInt8)(fB * 255.0)); + } + } - Bitmap impTransformBitmap( + Bitmap impTransformBitmap( const Bitmap& rSource, const Size aDestinationSize, const basegfx::B2DHomMatrix& rTransform, bool bSmooth) - { + { Bitmap aDestination(aDestinationSize, 24); BitmapWriteAccess* pWrite = aDestination.AcquireWriteAccess(); - if(pWrite) - { - const Size aContentSizePixel(rSource.GetSizePixel()); - BitmapReadAccess* pRead = (const_cast< Bitmap& >(rSource)).AcquireReadAccess(); + if(pWrite) + { + const Size aContentSizePixel(rSource.GetSizePixel()); + BitmapReadAccess* pRead = (const_cast< Bitmap& >(rSource)).AcquireReadAccess(); - if(pRead) - { - const Size aDestinationSizePixel(aDestination.GetSizePixel()); - bool bWorkWithIndex(rSource.GetBitCount() <= 8); - BitmapColor aOutside(BitmapColor(0xff, 0xff, 0xff)); + if(pRead) + { + const Size aDestinationSizePixel(aDestination.GetSizePixel()); + const BitmapColor aOutside(BitmapColor(0xff, 0xff, 0xff)); - for(sal_Int32 y(0L); y < aDestinationSizePixel.getHeight(); y++) - { - for(sal_Int32 x(0L); x < aDestinationSizePixel.getWidth(); x++) - { - const basegfx::B2DPoint aSourceCoor(rTransform * basegfx::B2DPoint(x, y)); - const sal_Int32 nIntX(basegfx::fround(aSourceCoor.getX())); + for(sal_Int32 y(0L); y < aDestinationSizePixel.getHeight(); y++) + { + for(sal_Int32 x(0L); x < aDestinationSizePixel.getWidth(); x++) + { + const basegfx::B2DPoint aSourceCoor(rTransform * basegfx::B2DPoint(x, y)); - if(nIntX >= 0L && nIntX < aContentSizePixel.getWidth()) - { - const sal_Int32 nIntY(basegfx::fround(aSourceCoor.getY())); + if(aSourceCoor.getX() >= 0.0) + { + const sal_Int32 nIntX(static_cast< sal_Int32 >(aSourceCoor.getX())); - if(nIntY >= 0L && nIntY < aContentSizePixel.getHeight()) - { - // inside pixel - BitmapColor aValue; - - if(bWorkWithIndex) + if(nIntX < aContentSizePixel.getWidth()) + { + if(aSourceCoor.getY() >= 0.0) { - aValue = pRead->GetPaletteColor(pRead->GetPixelIndex(nIntY, nIntX)); - } - else - { - aValue = pRead->GetPixel(nIntY, nIntX); - } + const sal_Int32 nIntY(static_cast< sal_Int32 >(aSourceCoor.getY())); - if(bSmooth) - { - impSmoothPoint(aValue, aSourceCoor, nIntX, nIntY, *pRead); + if(nIntY < aContentSizePixel.getHeight()) + { + // inside pixel + BitmapColor aValue(pRead->GetColor(nIntY, nIntX)); + + if(bSmooth) + { + impSmoothPoint(aValue, aSourceCoor, nIntX, nIntY, *pRead); + } + + pWrite->SetPixel(y, x, aValue); + continue; + } } + } + } - pWrite->SetPixel(y, x, aValue); - continue; - } - } + // here are outside pixels. Complete mask + pWrite->SetPixel(y, x, aOutside); + } + } - // here are outside pixels. Complete mask - if(bWorkWithIndex) - { - pWrite->SetPixel(y, x, aOutside); - } - } - } + delete pRead; + } - delete pRead; - } + delete pWrite; + } - delete pWrite; - } - rSource.AdaptBitCount(aDestination); return aDestination; - } + } } // end of anonymous namespace + BitmapEx BitmapEx::TransformBitmapEx( double fWidth, double fHeight, - const basegfx::B2DHomMatrix& rTransformation) const + const basegfx::B2DHomMatrix& rTransformation, + bool bSmooth) const { if(fWidth <= 1 || fHeight <= 1) return BitmapEx(); // force destination to 24 bit, we want to smooth output const Size aDestinationSize(basegfx::fround(fWidth), basegfx::fround(fHeight)); - static bool bDoSmoothAtAll(true); - const Bitmap aDestination(impTransformBitmap(GetBitmap(), aDestinationSize, rTransformation, bDoSmoothAtAll)); + const Bitmap aDestination(impTransformBitmap(GetBitmap(), aDestinationSize, rTransformation, bSmooth)); // create mask if(IsTransparent()) { if(IsAlpha()) { - const Bitmap aAlpha(impTransformBitmap(GetAlpha().GetBitmap(), aDestinationSize, rTransformation, bDoSmoothAtAll)); + const Bitmap aAlpha(impTransformBitmap(GetAlpha().GetBitmap(), aDestinationSize, rTransformation, bSmooth)); return BitmapEx(aDestination, AlphaMask(aAlpha)); } else @@ -1027,7 +1027,9 @@ BitmapEx BitmapEx::getTransformed( const basegfx::B2DHomMatrix& rTransformation, - double fMaximumArea) const + const basegfx::B2DRange& rVisibleRange, + double fMaximumArea, + bool bSmooth) const { BitmapEx aRetval; @@ -1040,16 +1042,27 @@ if(!nSourceWidth || !nSourceHeight) return aRetval; - // Get dest range + // Get aOutlineRange basegfx::B2DRange aOutlineRange(0.0, 0.0, 1.0, 1.0); + aOutlineRange.transform(rTransformation); - // get target size - double fWidth(aOutlineRange.getWidth()); - double fHeight(aOutlineRange.getHeight()); + // create visible range from it by moving from relative to absolute + basegfx::B2DRange aVisibleRange(rVisibleRange); + aVisibleRange.transform( + basegfx::tools::createScaleTranslateB2DHomMatrix( + aOutlineRange.getRange(), + aOutlineRange.getMinimum())); + + // get target size (which is visible range's size) + double fWidth(aVisibleRange.getWidth()); + double fHeight(aVisibleRange.getHeight()); + if(fWidth < 1.0 || fHeight < 1.0) + { return aRetval; + } // test if discrete size (pixel) maybe too big and limit it const double fArea(fWidth * fHeight); @@ -1074,8 +1087,10 @@ // aOutlineRange aTransform = rTransformation * aTransform; - // substract top-left of aOutlineRange - aTransform.translate(-aOutlineRange.getMinX(), -aOutlineRange.getMinY()); + // substract top-left of absolute VisibleRange + aTransform.translate( + -aVisibleRange.getMinX(), + -aVisibleRange.getMinY()); // scale to target pixels (if needed) if(bNeedToReduce) @@ -1087,7 +1102,7 @@ aTransform.invert(); // create bitmap using source, destination and linear back-transformation - aRetval = TransformBitmapEx(fWidth, fHeight, aTransform); + aRetval = TransformBitmapEx(fWidth, fHeight, aTransform, bSmooth); return aRetval; } Index: source/gdi/outdev2.cxx =================================================================== --- source/gdi/outdev2.cxx (revision 1502181) +++ source/gdi/outdev2.cxx (working copy) @@ -43,6 +43,7 @@ #include #include #include +#include #define BAND_MAX_SIZE 512000 @@ -818,8 +819,9 @@ const bool bSheared(!basegfx::fTools::equalZero(fShearX)); const bool bMirroredX(basegfx::fTools::less(aScale.getX(), 0.0)); const bool bMirroredY(basegfx::fTools::less(aScale.getY(), 0.0)); + static bool bForceToOwnTransformer(false); - if(!bRotated && !bSheared && !bMirroredX && !bMirroredY) + if(!bForceToOwnTransformer && !bRotated && !bSheared && !bMirroredX && !bMirroredY) { // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx // do *not* execute the mirroring here, it's done in the fallback @@ -840,7 +842,7 @@ const basegfx::B2DHomMatrix aFullTransform(GetViewTransformation() * rTransformation); const bool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile && !bPrinter); - if(bTryDirectPaint) + if(!bForceToOwnTransformer && bTryDirectPaint) { // try to paint directly const basegfx::B2DPoint aNull(aFullTransform * basegfx::B2DPoint(0.0, 0.0)); @@ -873,7 +875,7 @@ if(!bDone) { // take the fallback when no rotate and shear, but mirror (else we would have done this above) - if(!bRotated && !bSheared) + if(!bForceToOwnTransformer && !bRotated && !bSheared) { // with no rotation or shear it can be mapped to DrawBitmapEx // do *not* execute the mirroring here, it's done in the fallback @@ -886,14 +888,105 @@ // fallback; create transformed bitmap the hard way (back-transform // the pixels) and paint - basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0); - const double fMaximumArea(bMetafile ? 800000.0 : 200000.0); - const BitmapEx aTransformed(rBitmapEx.getTransformed(aFullTransform, fMaximumArea)); - aTargetRange.transform(rTransformation); - const Point aDestPt(basegfx::fround(aTargetRange.getMinX()), basegfx::fround(aTargetRange.getMinY())); - const Size aDestSize(basegfx::fround(aTargetRange.getWidth()), basegfx::fround(aTargetRange.getHeight())); + basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0); - DrawBitmapEx(aDestPt, aDestSize, aTransformed); + if(!bMetafile && !bPrinter) + { + // limit TargetRange to existing pixels (if pixel device) + // first get discrete range of object + basegfx::B2DRange aFullPixelRange(aVisibleRange); + + aFullPixelRange.transform(aFullTransform); + + if(basegfx::fTools::equalZero(aFullPixelRange.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange.getHeight())) + { + // object is outside of visible area + return; + } + + // now get discrete target pixels; start with OutDev pixel size and evtl. + // intersect with active clipping area + basegfx::B2DRange aOutPixel( + 0.0, + 0.0, + GetOutputSizePixel().Width(), + GetOutputSizePixel().Height()); + + if(IsClipRegion()) + { + const Rectangle aRegionRectangle(GetActiveClipRegion().GetBoundRect()); + + aOutPixel.intersect( // caution! Range from rectangle, one too much (!) + basegfx::B2DRange( + aRegionRectangle.Left(), + aRegionRectangle.Top(), + aRegionRectangle.Right() + 1, + aRegionRectangle.Bottom() + 1)); + } + + if(aOutPixel.isEmpty()) + { + // no active output area + return; + } + + // if aFullPixelRange is not completely inside of aOutPixel, + // reduction of target pixels is possible + if(!aOutPixel.isInside(aFullPixelRange)) + { + basegfx::B2DRange aVisiblePixelRange(aFullPixelRange); + + aVisiblePixelRange.intersect(aOutPixel); + + if(aVisiblePixelRange.isEmpty()) + { + // nothing in visible part, reduces to nothing + return; + } + + // aVisiblePixelRange contains the reduced output area in + // discrete coordinates. To make it useful everywhere, make it relative to + // the object range + basegfx::B2DHomMatrix aMakeVisiblePixelRangeRelative; + + aMakeVisiblePixelRangeRelative.translate( + -aFullPixelRange.getMinX(), + -aFullPixelRange.getMinY()); + aMakeVisiblePixelRangeRelative.scale( + 1.0 / aFullPixelRange.getWidth(), + 1.0 / aFullPixelRange.getHeight()); + aVisiblePixelRange.transform(aMakeVisiblePixelRangeRelative); + aVisibleRange = aVisiblePixelRange; + } + } + + if(!aVisibleRange.isEmpty()) + { + const double fMaximumArea(bMetafile ? 800000.0 : 200000.0); + static bool bDoSmoothAtAll(true); + const BitmapEx aTransformed( + rBitmapEx.getTransformed( + aFullTransform, + aVisibleRange, + fMaximumArea, + bDoSmoothAtAll)); + basegfx::B2DRange aTargetRange(0.0, 0.0, 1.0, 1.0); + + // get logic object target range + aTargetRange.transform(rTransformation); + + // get from unified/relative VisibleRange to logoc one + aVisibleRange.transform( + basegfx::tools::createScaleTranslateB2DHomMatrix( + aTargetRange.getRange(), + aTargetRange.getMinimum())); + + // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose + const Point aDestPt(basegfx::fround(aVisibleRange.getMinX()), basegfx::fround(aVisibleRange.getMinY())); + const Size aDestSize(basegfx::fround(aVisibleRange.getWidth()), basegfx::fround(aVisibleRange.getHeight())); + + DrawBitmapEx(aDestPt, aDestSize, aTransformed); + } } }