Kaydet (Commit) 75e90107 authored tarafından Armin Le Grand's avatar Armin Le Grand

i122778 Enhanced own transformer for drawing transformed bitmaps which is used…

i122778 Enhanced own transformer for drawing transformed bitmaps which is used in the cases where no fallback for direct system support is there (Linux)
üst 5d963c0c
......@@ -393,25 +393,42 @@ public:
@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
......
......@@ -171,6 +171,16 @@ public:
inline BitmapColor GetColor( long nY, long nX ) const;
inline sal_uInt8 GetPixelIndex( long nY, long nX ) const;
inline sal_uInt8 GetLuminance( long nY, long nX ) const;
/** Get the interpolated color at coordinates fY, fX; if outside, return rFallback */
BitmapColor GetInterpolatedColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const;
/** Get the color at coordinates fY, fX; if outside, return rFallback. Automatically does the correct
inside/outside checks, e.g. static_cast< sal_uInt32 >(-0.25) *is* 0, not -1 and has to be outside */
BitmapColor GetColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const;
/** Get the color at coordinates nY, nX; if outside, return rFallback */
BitmapColor GetColorWithFallback( long nY, long nX, const BitmapColor& rFallback ) const;
};
// ---------------------
......
......@@ -837,87 +837,6 @@ sal_uInt8 BitmapEx::GetTransparency(sal_Int32 nX, sal_Int32 nY) const
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);
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)
{
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);
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);
}
}
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);
}
rValue.SetRed((sal_uInt8)(fR * 255.0));
rValue.SetGreen((sal_uInt8)(fG * 255.0));
rValue.SetBlue((sal_uInt8)(fB * 255.0));
}
}
Bitmap impTransformBitmap(
const Bitmap& rSource,
const Size aDestinationSize,
......@@ -929,54 +848,41 @@ namespace
if(pWrite)
{
const Size aContentSizePixel(rSource.GetSizePixel());
//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));
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()));
if(nIntX >= 0L && nIntX < aContentSizePixel.getWidth())
if(bSmooth)
{
const sal_Int32 nIntY(basegfx::fround(aSourceCoor.getY()));
if(nIntY >= 0L && nIntY < aContentSizePixel.getHeight())
{
// inside pixel
BitmapColor aValue;
if(bWorkWithIndex)
{
aValue = pRead->GetPaletteColor(pRead->GetPixelIndex(nIntY, nIntX));
}
else
{
aValue = pRead->GetPixel(nIntY, nIntX);
}
if(bSmooth)
{
impSmoothPoint(aValue, aSourceCoor, nIntX, nIntY, *pRead);
}
pWrite->SetPixel(y, x, aValue);
continue;
}
pWrite->SetPixel(
y,
x,
pRead->GetInterpolatedColorWithFallback(
aSourceCoor.getY(),
aSourceCoor.getX(),
aOutside));
}
// here are outside pixels. Complete mask
if(bWorkWithIndex)
else
{
pWrite->SetPixel(y, x, aOutside);
// this version does the correct <= 0.0 checks, so no need
// to do the static_cast< sal_Int32 > self and make an error
pWrite->SetPixel(
y,
x,
pRead->GetColorWithFallback(
aSourceCoor.getY(),
aSourceCoor.getX(),
aOutside));
}
}
}
......@@ -992,25 +898,26 @@ namespace
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 +934,9 @@ BitmapEx BitmapEx::TransformBitmapEx(
BitmapEx BitmapEx::getTransformed(
const basegfx::B2DHomMatrix& rTransformation,
double fMaximumArea) const
const basegfx::B2DRange& rVisibleRange,
double fMaximumArea,
bool bSmooth) const
{
BitmapEx aRetval;
......@@ -1040,20 +949,31 @@ BitmapEx BitmapEx::getTransformed(
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);
const bool bNeedToReduce(fArea > fMaximumArea);
const bool bNeedToReduce(basegfx::fTools::more(fArea, fMaximumArea));
double fReduceFactor(1.0);
if(bNeedToReduce)
......@@ -1074,8 +994,10 @@ BitmapEx BitmapEx::getTransformed(
// 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 +1009,7 @@ BitmapEx BitmapEx::getTransformed(
aTransform.invert();
// create bitmap using source, destination and linear back-transformation
aRetval = TransformBitmapEx(fWidth, fHeight, aTransform);
aRetval = TransformBitmapEx(fWidth, fHeight, aTransform, bSmooth);
return aRetval;
}
......
......@@ -324,6 +324,126 @@ sal_uInt16 BitmapReadAccess::GetBestPaletteIndex( const BitmapColor& rBitmapColo
return( HasPalette() ? mpBuffer->maPalette.GetBestIndex( rBitmapColor ) : 0 );
}
BitmapColor BitmapReadAccess::GetInterpolatedColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const
{
// ask directly doubles >= 0.0 here to avoid rounded values of 0 at small negative
// double values, e.g. static_cast< sal_Int32 >(-0.25) is 0, not -1, but *has* to be outside (!)
if(mpBuffer && fX >= 0.0 && fY >= 0.0)
{
const sal_Int32 nX(static_cast< sal_Int32 >(fX));
const sal_Int32 nY(static_cast< sal_Int32 >(fY));
if(nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
{
// get base-return value from inside pixel
BitmapColor aRetval(GetColor(nY, nX));
// calculate deltas and indices for neighbour accesses
sal_Int16 nDeltaX((fX - (nX + 0.5)) * 255.0); // [-255 .. 255]
sal_Int16 nDeltaY((fY - (nY + 0.5)) * 255.0); // [-255 .. 255]
sal_Int16 nIndX(0);
sal_Int16 nIndY(0);
if(nDeltaX > 0)
{
nIndX = nX + 1;
}
else
{
nIndX = nX - 1;
nDeltaX = -nDeltaX;
}
if(nDeltaY > 0)
{
nIndY = nY + 1;
}
else
{
nIndY = nY - 1;
nDeltaY = -nDeltaY;
}
// get right/left neighbour
BitmapColor aXCol(rFallback);
if(nDeltaX && nIndX >= 0 && nIndX < mpBuffer->mnWidth)
{
aXCol = GetColor(nY, nIndX);
}
// get top/bottom neighbour
BitmapColor aYCol(rFallback);
if(nDeltaY && nIndY >= 0 && nIndY < mpBuffer->mnHeight)
{
aYCol = GetColor(nIndY, nX);
}
// get one of four edge neighbours
BitmapColor aXYCol(rFallback);
if(nDeltaX && nDeltaY && nIndX >=0 && nIndY >= 0 && nIndX < mpBuffer->mnWidth && nIndY < mpBuffer->mnHeight)
{
aXYCol = GetColor(nIndY, nIndX);
}
// merge return value with right/left neighbour
if(aXCol != aRetval)
{
aRetval.Merge(aXCol, 255 - nDeltaX);
}
// merge top/bottom neighbour with edge
if(aYCol != aXYCol)
{
aYCol.Merge(aXYCol, 255 - nDeltaX);
}
// merge return value with already merged top/bottom neighbour
if(aRetval != aYCol)
{
aRetval.Merge(aYCol, 255 - nDeltaY);
}
return aRetval;
}
}
return rFallback;
}
BitmapColor BitmapReadAccess::GetColorWithFallback( double fY, double fX, const BitmapColor& rFallback ) const
{
// ask directly doubles >= 0.0 here to avoid rounded values of 0 at small negative
// double values, e.g. static_cast< sal_Int32 >(-0.25) is 0, not -1, but *has* to be outside (!)
if(mpBuffer && fX >= 0.0 && fY >= 0.0)
{
const sal_Int32 nX(static_cast< sal_Int32 >(fX));
const sal_Int32 nY(static_cast< sal_Int32 >(fY));
if(nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
{
return GetColor(nY, nX);
}
}
return rFallback;
}
BitmapColor BitmapReadAccess::GetColorWithFallback( long nY, long nX, const BitmapColor& rFallback ) const
{
if(mpBuffer)
{
if(nX >= 0 && nY >= 0 && nX < mpBuffer->mnWidth && nY < mpBuffer->mnHeight)
{
return GetColor(nY, nX);
}
}
return rFallback;
}
// ---------------------
// - BitmapWriteAccess -
// ---------------------
......
......@@ -43,6 +43,7 @@
#include <window.h>
#include <outdata.hxx>
#include <basegfx/matrix/b2dhommatrix.hxx>
#include <basegfx/matrix/b2dhommatrixtools.hxx>
#define BAND_MAX_SIZE 512000
......@@ -818,8 +819,9 @@ void OutputDevice::DrawTransformedBitmapEx(
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 @@ void OutputDevice::DrawTransformedBitmapEx(
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 @@ void OutputDevice::DrawTransformedBitmapEx(
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,115 @@ void OutputDevice::DrawTransformedBitmapEx(
// 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()));
DrawBitmapEx(aDestPt, aDestSize, aTransformed);
basegfx::B2DRange aVisibleRange(0.0, 0.0, 1.0, 1.0);
// limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
double fMaximumArea(1000000.0);
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
basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
if(!aOutPixel.isInside(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 aMakeVisibleRangeRelative;
aVisibleRange = aVisiblePixelRange;
aMakeVisibleRangeRelative.translate(
-aFullPixelRange.getMinX(),
-aFullPixelRange.getMinY());
aMakeVisibleRangeRelative.scale(
1.0 / aFullPixelRange.getWidth(),
1.0 / aFullPixelRange.getHeight());
aVisibleRange.transform(aMakeVisibleRangeRelative);
}
// for pixel devices, do *not* limit size, else OutputDevice::ImplDrawAlpha
// will create another, badly scaled bitmap to do the job. Nonetheless, do a
// maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
// errors in rough estimations
const double fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
fMaximumArea = std::min(4096000.0, fNewMaxArea + 1.0);
}
if(!aVisibleRange.isEmpty())
{
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);
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment