bool OutputDevice::TransformAndReduceBitmapExToTargetRange( const basegfx::B2DHomMatrix& aFullTransform,
basegfx::B2DRange &aVisibleRange, double &fMaximumArea)
{ // 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 returnfalse;
}
// 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());
// caution! Range from rectangle, one too much (!)
aRegionRectangle.AdjustRight(-1);
aRegionRectangle.AdjustBottom(-1);
aOutPixel.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle) );
}
if(aOutPixel.isEmpty())
{ // no active output area returnfalse;
}
// if aFullPixelRange is not completely inside of aOutPixel, // reduction of target pixels is possible
basegfx::B2DRange aVisiblePixelRange(aFullPixelRange);
if(aVisiblePixelRange.isEmpty())
{ // nothing in visible part, reduces to nothing returnfalse;
}
// aVisiblePixelRange contains the reduced output area in // discrete coordinates. To make it useful everywhere, make it relative to // the object range
basegfx::B2DHomMatrix aMakeVisibleRangeRelative;
// for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap // 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 constdouble fNewMaxArea(aVisiblePixelRange.getWidth() * aVisiblePixelRange.getHeight());
// MM02 add some test class to get a simple timer-based output to be able // to check if it gets faster - and how much. Uncomment next line or set // DO_TIME_TEST for compile time if you want to use it // #define DO_TIME_TEST #ifdef DO_TIME_TEST #include <tools/time.hxx> struct LocalTimeTest
{ const sal_uInt64 nStartTime;
LocalTimeTest() : nStartTime(tools::Time::GetSystemTicks()) {}
~LocalTimeTest()
{ const sal_uInt64 nEndTime(tools::Time::GetSystemTicks()); const sal_uInt64 nDiffTime(nEndTime - nStartTime);
// MM02 compared to other public methods of OutputDevice // this test was missing and led to zero-ptr-accesses if ( !mpGraphics && !AcquireGraphics() ) return;
#ifdef DO_TIME_TEST // MM02 start time test when some data (not for trivial stuff). Will // trigger and show data when leaving this method by destructing helper staticconstchar* pEnableBitmapDrawTimerTimer(getenv("SAL_ENABLE_TIMER_BITMAPDRAW")); staticbool bUseTimer(nullptr != pEnableBitmapDrawTimerTimer);
std::unique_ptr<LocalTimeTest> aTimeTest(
bUseTimer && rBitmapEx.GetSizeBytes() > 10000
? new LocalTimeTest()
: nullptr); #endif
BitmapEx bitmapEx = rBitmapEx;
constbool bInvert(RasterOp::Invert == meRasterOp); constbool bBitmapChangedColor(mnDrawMode & (DrawModeFlags::BlackBitmap | DrawModeFlags::WhiteBitmap | DrawModeFlags::GrayBitmap )); constbool bTryDirectPaint(!bInvert && !bBitmapChangedColor && !bMetafile); // tdf#130768 CAUTION(!) using GetViewTransformation() is *not* enough here, it may // be that mnOutOffX/mnOutOffY is used - see AOO bug 75163, mentioned at // ImplGetDeviceTransformation declaration
basegfx::B2DHomMatrix aFullTransform(ImplGetDeviceTransformation() * rTransformation);
// First try to handle additional alpha blending, either directly, or modify the bitmap. if(!rtl::math::approxEqual( fAlpha, 1.0 ))
{ if(bTryDirectPaint)
{ if(DrawTransformBitmapExDirect(aFullTransform, bitmapEx, fAlpha))
{ // we are done return;
}
} // Apply the alpha manually.
sal_uInt8 nTransparency( static_cast<sal_uInt8>( ::basegfx::fround( 255.0*(1.0 - fAlpha) + .5) ) );
AlphaMask aAlpha( bitmapEx.GetSizePixel(), &nTransparency ); if( bitmapEx.IsAlpha())
aAlpha.BlendWith( bitmapEx.GetAlphaMask());
bitmapEx = BitmapEx( bitmapEx.GetBitmap(), aAlpha );
}
// If the backend's implementation is known to not need any optimizations here, pass to it directly. // With most backends it's more performant to try to simplify to DrawBitmapEx() first. if(bTryDirectPaint && mpGraphics->HasFastDrawTransformedBitmap() && DrawTransformBitmapExDirect(aFullTransform, bitmapEx)) return;
if(!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 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
Point aDestPt(basegfx::fround<tools::Long>(aTranslate.getX()), basegfx::fround<tools::Long>(aTranslate.getY())); const Size aDestSize(
basegfx::fround<tools::Long>(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
basegfx::fround<tools::Long>(aScale.getY() + aTranslate.getY()) - aDestPt.Y()); const Point aOrigin = GetMapMode().GetOrigin(); if (!bMetafile && comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel)
{
aDestPt.Move(aOrigin.getX(), aOrigin.getY());
EnableMapMode(false);
}
// Try the backend's implementation before resorting to the slower fallback here. if(bTryDirectPaint && DrawTransformBitmapExDirect(aFullTransform, bitmapEx)) return;
// take the fallback when no rotate and shear, but mirror (else we would have done this above) if(!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 // #i124580# the correct DestSize needs to be calculated based on MaxXY values const Point aDestPt(basegfx::fround<tools::Long>(aTranslate.getX()), basegfx::fround<tools::Long>(aTranslate.getY())); const Size aDestSize(
basegfx::fround<tools::Long>(aScale.getX() + aTranslate.getX()) - aDestPt.X(),
basegfx::fround<tools::Long>(aScale.getY() + aTranslate.getY()) - aDestPt.Y());
// at this point we are either sheared or rotated or both
assert(bSheared || bRotated);
// fallback; create transformed bitmap the hard way (back-transform // the pixels) and paint
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) // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum // to avoid crashes/resource problems (ca. 1500x3000 here) const Size& rOriginalSizePixel(bitmapEx.GetSizePixel()); constdouble fOrigArea(rOriginalSizePixel.Width() * rOriginalSizePixel.Height() * 0.5); constdouble fOrigAreaScaled(fOrigArea * 1.44); double fMaximumArea(std::clamp(fOrigAreaScaled, 1000000.0, 4500000.0));
// #122923# when the result needs an alpha channel due to being rotated or sheared // and thus uncovering areas, add these channels so that the own transformer (used // in getTransformed) also creates a transformed alpha channel if(!aTransformed.IsAlpha() && (bSheared || bRotated))
{ // parts will be uncovered, extend aTransformed with a mask bitmap const Bitmap aContent(aTransformed.GetBitmap());
basegfx::B2DVector aFullScale, aFullTranslate; double fFullRotate, fFullShearX;
aFullTransform.decompose(aFullScale, aFullTranslate, fFullRotate, fFullShearX); if (aFullScale.getX() > 0 && aFullScale.getY() > 0
&& rOriginalSizePixel.getWidth() > aFullScale.getX()
&& rOriginalSizePixel.getHeight() > aFullScale.getY())
{ // aFullTransform would downscale the bitmap: avoid this, so the recorded metafile can be // better upscaled later.
basegfx::B2DHomMatrix aTransform = basegfx::utils::createScaleB2DHomMatrix(
rOriginalSizePixel.getWidth() / aFullScale.getX(),
rOriginalSizePixel.getHeight() / aFullScale.getY());
aFullTransform *= aTransform;
}
double fSourceRatio = 1.0; if (rOriginalSizePixel.getHeight() != 0)
{
fSourceRatio = rOriginalSizePixel.getWidth() / rOriginalSizePixel.getHeight();
} double fTargetRatio = 1.0; if (aFullScale.getY() != 0)
{
fTargetRatio = aFullScale.getX() / aFullScale.getY();
} bool bAspectRatioKept = rtl::math::approxEqual(fSourceRatio, fTargetRatio); if (bSheared || !bAspectRatioKept)
{ // Not only rotation, or scaling does not keep aspect ratio.
aTransformed = aTransformed.getTransformed(
aFullTransform,
aVisibleRange,
fMaximumArea);
} else
{ // Just rotation, can do that directly.
fFullRotate = fmod(fFullRotate * -1, 2 * M_PI); if (fFullRotate < 0)
{
fFullRotate += 2 * M_PI;
}
Degree10 nAngle10(basegfx::fround(basegfx::rad2deg<10>(fFullRotate)));
aTransformed.Rotate(nAngle10, COL_TRANSPARENT);
}
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::utils::createScaleTranslateB2DHomMatrix(
aTargetRange.getRange(),
aTargetRange.getMinimum()));
// extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose // #i124580# the correct DestSize needs to be calculated based on MaxXY values const Point aDestPt(basegfx::fround<tools::Long>(aVisibleRange.getMinX()), basegfx::fround<tools::Long>(aVisibleRange.getMinY())); const Size aDestSize(
basegfx::fround<tools::Long>(aVisibleRange.getMaxX()) - aDestPt.X(),
basegfx::fround<tools::Long>(aVisibleRange.getMaxY()) - aDestPt.Y());
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.