Kaydet (Commit) 4b161067 authored tarafından Tomaž Vajngerl's avatar Tomaž Vajngerl

Stepwise rebuild bitmap rendering from scratch to avoid rendering bugs.

Bitmap rendering was rebuild from the original state and checked for
rendering bugs at every change. Currently the implementation supports
scaling by averagin for RGB channels an for alpha channel in some
cases. For scaling factor > 0.6, the original bilinear scaling is
used. Implementation is currently still in "outdev2" but is decoupled
and will be moved to its proper place into "bitmap" and "bitmapex".

Change-Id: I6feb744712956a92d6140d079dc3a85ee8511930
üst 35e13d1f
...@@ -415,12 +415,12 @@ public: ...@@ -415,12 +415,12 @@ public:
double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount ); double* pBlurVector, double*& pWeights, int*& pPixels, int*& pCount );
public: public:
Bitmap(); Bitmap();
Bitmap( const Bitmap& rBitmap ); Bitmap( const Bitmap& rBitmap );
Bitmap( const Size& rSizePixel, sal_uInt16 nBitCount, const BitmapPalette* pPal = NULL ); Bitmap( const Size& rSizePixel, sal_uInt16 nBitCount, const BitmapPalette* pPal = NULL );
Bitmap( const ResId& rResId ); Bitmap( const ResId& rResId );
Bitmap( SalBitmap* pSalBitmap ); Bitmap( SalBitmap* pSalBitmap );
~Bitmap(); ~Bitmap();
Bitmap& operator=( const Bitmap& rBitmap ); Bitmap& operator=( const Bitmap& rBitmap );
inline sal_Bool operator!() const; inline sal_Bool operator!() const;
...@@ -448,7 +448,6 @@ public: ...@@ -448,7 +448,6 @@ public:
*/ */
void SetSourceSizePixel( const Size& ); void SetSourceSizePixel( const Size& );
sal_uInt16 GetBitCount() const; sal_uInt16 GetBitCount() const;
inline sal_uLong GetColorCount() const; inline sal_uLong GetColorCount() const;
inline sal_uLong GetSizeBytes() const; inline sal_uLong GetSizeBytes() const;
...@@ -650,11 +649,6 @@ public: ...@@ -650,11 +649,6 @@ public:
*/ */
sal_Bool Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT ); sal_Bool Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT );
/** Scale, crop and rotate the bitmap */
sal_Bool ScaleCropRotate(
const double& rScaleX, const double& rScaleY, const Rectangle& rRectPixel, long nAngle10,
const Color& rFillColor, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT );
/** Rotate bitmap by the specified angle /** Rotate bitmap by the specified angle
@param nAngle10 @param nAngle10
......
...@@ -268,11 +268,6 @@ public: ...@@ -268,11 +268,6 @@ public:
*/ */
sal_Bool Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT ); sal_Bool Scale( const double& rScaleX, const double& rScaleY, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT );
/** Scale, crop and rotate the bitmap */
sal_Bool ScaleCropRotate(
const double& rScaleX, const double& rScaleY, const Rectangle& rRectPixel, long nAngle10,
const Color& rFillColor, sal_uLong nScaleFlag = BMP_SCALE_DEFAULT );
/** Rotate bitmap by the specified angle /** Rotate bitmap by the specified angle
@param nAngle10 @param nAngle10
......
...@@ -2313,237 +2313,4 @@ bool Bitmap::ImplConvolutionPass(Bitmap& aNewBitmap, const int nNewSize, BitmapR ...@@ -2313,237 +2313,4 @@ bool Bitmap::ImplConvolutionPass(Bitmap& aNewBitmap, const int nNewSize, BitmapR
return true; return true;
} }
sal_Bool Bitmap::ScaleCropRotate(
const double& rScaleX, const double& rScaleY, const Rectangle& rRectPixel, long nAngle10,
const Color& rFillColor, sal_uLong /* nScaleFlag */ )
{
bool bRet;
if ( rScaleX < 0.6 || rScaleY < 0.6 )
{
bRet = ImplTransformAveraging( rScaleX, rScaleY, rRectPixel, nAngle10, rFillColor);
}
else
{
bRet = ImplTransformBilinearFiltering( rScaleX, rScaleY, rRectPixel, nAngle10, rFillColor);
}
return bRet;
}
// Scaling algorithm best for shrinking below factor 0.5 where algorithms with limited sampling range show bad results (bilinear, bicubic).
// The algoritm determines the sampling range for one pixel and calculates the average of samples which is the resulting pixel.
bool Bitmap::ImplTransformAveraging( const double& rScaleX, const double& rScaleY, const Rectangle& rRotatedRectangle, const long nAngle10, const Color& rFillColor )
{
const Size aSizePix( GetSizePixel() );
const int nStartX = rRotatedRectangle.Left();
const int nStartY = rRotatedRectangle.Top();
const int nEndX = rRotatedRectangle.Right();
const int nEndY = rRotatedRectangle.Bottom();
const int nTargetWidth = rRotatedRectangle.GetWidth();
const int nTargetHeight = rRotatedRectangle.GetHeight();
const int nOriginWidth = aSizePix.Width();
const int nOriginHeight = aSizePix.Height();
const int nScaledWidth = FRound( nOriginWidth * rScaleX );
const int nScaledHeight = FRound( nOriginHeight * rScaleY );
const double aReverseScaleX = 1.0 / rScaleX;
const double aReverseScaleY = 1.0 / rScaleY;
const double fCosAngle = cos( nAngle10 * F_PI1800 );
const double fSinAngle = sin( nAngle10 * F_PI1800 );
if( nTargetWidth <= 1L || nTargetHeight <= 1L )
return false;
BitmapColor aColor, aResultColor;
Bitmap aOutBmp( Size( nTargetWidth, nTargetHeight ), 24 );
BitmapReadAccess* pReadAccess = AcquireReadAccess();
BitmapWriteAccess* pWriteAccess = aOutBmp.AcquireWriteAccess();
if( !pReadAccess || !pWriteAccess )
return false;
const BitmapColor aFillColor( pWriteAccess->GetBestMatchingColor( rFillColor ) );
int x, y, xOut, yOut;
int aCount;
double aSumRed, aSumGreen, aSumBlue;
for( y = nStartY, yOut = 0; y <= nEndY; y++, yOut++ )
{
for( x = nStartX, xOut = 0; x <= nEndX; x++, xOut++ )
{
double unrotatedX = fCosAngle * x - fSinAngle * y;
double unrotatedY = fSinAngle * x + fCosAngle * y;
if ( unrotatedX < 0
|| unrotatedX > nScaledWidth
|| unrotatedY < 0
|| unrotatedY > nScaledHeight)
{
pWriteAccess->SetPixel( yOut, xOut, aFillColor );
}
else
{
double dYStart = ((unrotatedY + 0.5) * aReverseScaleY) - 0.5;
double dYEnd = ((unrotatedY + 1.5) * aReverseScaleY) - 0.5;
int yStart = MinMax( dYStart, 0, nOriginHeight - 1);
int yEnd = MinMax( dYEnd, 0, nOriginHeight - 1);
double dXStart = ((unrotatedX + 0.5) * aReverseScaleX) - 0.5;
double dXEnd = ((unrotatedX + 1.5) * aReverseScaleX) - 0.5;
int xStart = MinMax( dXStart, 0, nOriginWidth - 1);
int xEnd = MinMax( dXEnd, 0, nOriginWidth - 1);
aSumRed = aSumGreen = aSumBlue = 0.0;
aCount = 0;
for (int yIn = yStart; yIn <= yEnd; yIn++)
{
for (int xIn = xStart; xIn <= xEnd; xIn++)
{
aColor = pReadAccess->GetPixel( yIn, xIn );
if( pReadAccess->HasPalette() )
aColor = pReadAccess->GetPaletteColor( aColor );
aSumRed += aColor.GetRed();
aSumGreen += aColor.GetGreen();
aSumBlue += aColor.GetBlue();
aCount++;
}
}
aResultColor.SetRed( MinMax( aSumRed / aCount, 0, 255) );
aResultColor.SetGreen( MinMax( aSumGreen / aCount, 0, 255) );
aResultColor.SetBlue( MinMax( aSumBlue / aCount, 0, 255) );
pWriteAccess->SetPixel( yOut, xOut, aResultColor );
}
}
}
ReleaseAccess( pReadAccess );
aOutBmp.ReleaseAccess( pWriteAccess );
ImplAssignWithSize( aOutBmp );
return true;
}
// Bilinear filtering used for shrinking and enlarging the source bitmap. Filtering is also used for rotation.
// Filtering shows bad results at shrinking for a factor less than 0.5 because of limited sampling.
bool Bitmap::ImplTransformBilinearFiltering( const double& rScaleX, const double& rScaleY, const Rectangle& rRotatedRectangle, const long nAngle10, const Color& rFillColor )
{
const int nOriginWidth = GetSizePixel().Width();
const int nOriginHeight = GetSizePixel().Height();
const int nScaledWidth = FRound( nOriginWidth * rScaleX );
const int nScaledHeight = FRound( nOriginHeight * rScaleY );
const int nTargetWidth = rRotatedRectangle.GetWidth();
const int nTargetHeight = rRotatedRectangle.GetHeight();
const int nStartX = rRotatedRectangle.Left();
const int nEndX = rRotatedRectangle.Right();
const int nStartY = rRotatedRectangle.Top();
const int nEndY = rRotatedRectangle.Bottom();
const double fCosAngle = cos( nAngle10 * F_PI1800 );
const double fSinAngle = sin( nAngle10 * F_PI1800 );
Bitmap aOutBmp( Size( nTargetWidth, nTargetHeight ), 24 );
BitmapReadAccess* pReadAccess = AcquireReadAccess();
BitmapWriteAccess* pWriteAccess = aOutBmp.AcquireWriteAccess();
if( !pReadAccess || !pWriteAccess )
return false;
const BitmapColor aFillColor( pWriteAccess->GetBestMatchingColor( rFillColor ) );
double aReverseScaleX = 1.0 / rScaleX;
double aReverseScaleY = 1.0 / rScaleY;
BitmapColor aColor00, aColor01, aColor10, aColor11, aResultColor;
int x, y, xOut, yOut;
for( y = nStartY, yOut = 0; y <= nEndY; y++, yOut++ )
{
for( x = nStartX, xOut = 0; x <= nEndX; x++, xOut++ )
{
double unrotatedX = fCosAngle * x - fSinAngle * y;
double unrotatedY = fSinAngle * x + fCosAngle * y;
if ( unrotatedX < 0
|| unrotatedX >= nScaledWidth
|| unrotatedY < 0
|| unrotatedY >= nScaledHeight)
{
pWriteAccess->SetPixel( yOut, xOut, aFillColor );
}
else
{
double sy0 = ((unrotatedY + 0.5) * aReverseScaleY) - 0.5;
int y0 = MinMax( floor( sy0 ), 0, nOriginHeight - 1);
int y1 = MinMax( y0 + 1, 0, nOriginHeight - 1);
double sx0 = ((unrotatedX + 0.5) * aReverseScaleX) - 0.5;
int x0 = MinMax( floor( sx0 ), 0, nOriginWidth - 1);
int x1 = MinMax( x0 + 1, 0, nOriginWidth - 1);
aColor00 = pReadAccess->GetPixel( y0, x0 );
aColor01 = pReadAccess->GetPixel( y1, x0 );
aColor10 = pReadAccess->GetPixel( y0, x1 );
aColor11 = pReadAccess->GetPixel( y1, x1 );
if( pReadAccess->HasPalette() )
{
aColor00 = pReadAccess->GetPaletteColor( aColor00 );
aColor01 = pReadAccess->GetPaletteColor( aColor01 );
aColor10 = pReadAccess->GetPaletteColor( aColor10 );
aColor11 = pReadAccess->GetPaletteColor( aColor11 );
}
double fx0 = sx0 - x0;
double fy0 = sy0 - y0;
double fx1 = 1.0 - fx0;
double fy1 = 1.0 - fy0;
double w00 = fx1 * fy1;
double w01 = fx1 * fy0;
double w10 = fx0 * fy1;
double w11 = fx0 * fy0;
double red = aColor00.GetRed() * w00 + aColor10.GetRed() * w10 + aColor01.GetRed() * w01 + aColor11.GetRed() * w11;
double green = aColor00.GetGreen() * w00 + aColor10.GetGreen() * w10 + aColor01.GetGreen() * w01 + aColor11.GetGreen() * w11;
double blue = aColor00.GetBlue() * w00 + aColor10.GetBlue() * w10 + aColor01.GetBlue() * w01 + aColor11.GetBlue() * w11;
aResultColor.SetRed( MinMax(red, 0, 255) );
aResultColor.SetGreen( MinMax(green, 0, 255) );
aResultColor.SetBlue( MinMax(blue, 0, 255) );
pWriteAccess->SetPixel( yOut, xOut, aResultColor );
}
}
}
ReleaseAccess( pReadAccess );
aOutBmp.ReleaseAccess( pWriteAccess );
ImplAssignWithSize( aOutBmp );
return true;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
...@@ -389,62 +389,6 @@ sal_Bool BitmapEx::Scale( const Size& rNewSize, sal_uLong nScaleFlag ) ...@@ -389,62 +389,6 @@ sal_Bool BitmapEx::Scale( const Size& rNewSize, sal_uLong nScaleFlag )
return bRet; return bRet;
} }
sal_Bool BitmapEx::ScaleCropRotate(
const double& rScaleX, const double& rScaleY, const Rectangle& rRectPixel,
long nAngle10, const Color& rFillColor, sal_uLong nScaleFlag )
{
bool bReturn = false;
if( !!aBitmap )
{
// If fill color is transpatent
const bool bTransparentRotation = Color( COL_TRANSPARENT ) == rFillColor;
// If angle is 0, 90, 180 or 270 degreees, then we don't need to create an alpha bitmap.
const bool bRightAngleRotation = (nAngle10 == 0 || nAngle10 == 900 || nAngle10 == 1800 || nAngle10 == 2700);
if( !bRightAngleRotation && bTransparentRotation )
{
if( eTransparent == TRANSPARENT_COLOR )
{
bReturn = aBitmap.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, aTransparentColor, nScaleFlag );
}
else if( eTransparent == TRANSPARENT_NONE )
{
bReturn = aBitmap.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, COL_BLACK, nScaleFlag );
if ( bReturn )
{
aMask = Bitmap( aBitmapSize, 1 );
aMask.Erase( COL_BLACK );
aMask.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, COL_WHITE, nScaleFlag );
eTransparent = TRANSPARENT_BITMAP;
}
} else {
bReturn = aBitmap.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, COL_BLACK, nScaleFlag );
if( bReturn && !!aMask )
{
aMask.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, COL_WHITE, nScaleFlag );
aMask.Convert( BMP_CONVERSION_8BIT_GREYS );
}
}
}
else
{
bReturn = aBitmap.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, rFillColor, nScaleFlag );
if( eTransparent == TRANSPARENT_BITMAP && !!aMask )
{
bReturn = aMask.ScaleCropRotate( rScaleX, rScaleY, rRectPixel, nAngle10, COL_WHITE, nScaleFlag );
aMask.Convert( BMP_CONVERSION_8BIT_GREYS );
}
}
}
if ( bReturn )
aBitmapSize = aBitmap.GetSizePixel();
return bReturn;
}
sal_Bool BitmapEx::Rotate( long nAngle10, const Color& rFillColor ) sal_Bool BitmapEx::Rotate( long nAngle10, const Color& rFillColor )
{ {
sal_Bool bRet = sal_False; sal_Bool bRet = sal_False;
......
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