1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
22 #include <vcl/bitmap.hxx>
23 #include <vcl/bitmapex.hxx>
24 #include <vcl/bitmapaccess.hxx>
25 #include <vcl/canvastools.hxx>
26 #include <vcl/gdimtf.hxx>
27 #include <vcl/metaact.hxx>
28 #include <config_features.h>
29 #if HAVE_FEATURE_OPENGL
30 #include <vcl/opengl/OpenGLHelper.hxx>
32 #include <vcl/outdev.hxx>
33 #include <vcl/virdev.hxx>
34 #include <vcl/image.hxx>
35 #include <vcl/BitmapMonochromeFilter.hxx>
37 #include <bmpfast.hxx>
41 #include <basegfx/matrix/b2dhommatrixtools.hxx>
43 #include <comphelper/lok.hxx>
44 #include <bitmapwriteaccess.hxx>
45 #include <sal/log.hxx>
46 #include <osl/diagnose.h>
47 #include <tools/helpers.hxx>
48 #include <tools/debug.hxx>
50 void OutputDevice::DrawBitmap( const Point
& rDestPt
, const Bitmap
& rBitmap
)
52 assert(!is_double_buffered_window());
54 const Size
aSizePix( rBitmap
.GetSizePixel() );
55 DrawBitmap( rDestPt
, PixelToLogic( aSizePix
), Point(), aSizePix
, rBitmap
, MetaActionType::BMP
);
58 void OutputDevice::DrawBitmap( const Point
& rDestPt
, const Size
& rDestSize
, const Bitmap
& rBitmap
)
60 assert(!is_double_buffered_window());
62 DrawBitmap( rDestPt
, rDestSize
, Point(), rBitmap
.GetSizePixel(), rBitmap
, MetaActionType::BMPSCALE
);
66 void OutputDevice::DrawBitmap( const Point
& rDestPt
, const Size
& rDestSize
,
67 const Point
& rSrcPtPixel
, const Size
& rSrcSizePixel
,
68 const Bitmap
& rBitmap
, const MetaActionType nAction
)
70 assert(!is_double_buffered_window());
72 if( ImplIsRecordLayout() )
75 if ( RasterOp::Invert
== meRasterOp
)
77 DrawRect( tools::Rectangle( rDestPt
, rDestSize
) );
81 Bitmap
aBmp( rBitmap
);
83 if ( mnDrawMode
& ( DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
|
84 DrawModeFlags::GrayBitmap
) )
86 if ( mnDrawMode
& ( DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
) )
90 if ( mnDrawMode
& DrawModeFlags::BlackBitmap
)
95 Color
aCol( cCmpVal
, cCmpVal
, cCmpVal
);
96 Push( PushFlags::LINECOLOR
| PushFlags::FILLCOLOR
);
99 DrawRect( tools::Rectangle( rDestPt
, rDestSize
) );
105 if ( mnDrawMode
& DrawModeFlags::GrayBitmap
)
106 aBmp
.Convert( BmpConversion::N8BitGreys
);
114 case MetaActionType::BMP
:
115 mpMetaFile
->AddAction( new MetaBmpAction( rDestPt
, aBmp
) );
118 case MetaActionType::BMPSCALE
:
119 mpMetaFile
->AddAction( new MetaBmpScaleAction( rDestPt
, rDestSize
, aBmp
) );
122 case MetaActionType::BMPSCALEPART
:
123 mpMetaFile
->AddAction( new MetaBmpScalePartAction(
124 rDestPt
, rDestSize
, rSrcPtPixel
, rSrcSizePixel
, aBmp
) );
131 if ( !IsDeviceOutputNecessary() )
134 if ( !mpGraphics
&& !AcquireGraphics() )
137 if ( mbInitClipRegion
)
140 if ( mbOutputClipped
)
143 if( !aBmp
.IsEmpty() )
145 SalTwoRect
aPosAry(rSrcPtPixel
.X(), rSrcPtPixel
.Y(), rSrcSizePixel
.Width(), rSrcSizePixel
.Height(),
146 ImplLogicXToDevicePixel(rDestPt
.X()), ImplLogicYToDevicePixel(rDestPt
.Y()),
147 ImplLogicWidthToDevicePixel(rDestSize
.Width()),
148 ImplLogicHeightToDevicePixel(rDestSize
.Height()));
150 if ( aPosAry
.mnSrcWidth
&& aPosAry
.mnSrcHeight
&& aPosAry
.mnDestWidth
&& aPosAry
.mnDestHeight
)
152 const BmpMirrorFlags nMirrFlags
= AdjustTwoRect( aPosAry
, aBmp
.GetSizePixel() );
154 if ( nMirrFlags
!= BmpMirrorFlags::NONE
)
155 aBmp
.Mirror( nMirrFlags
);
157 if ( aPosAry
.mnSrcWidth
&& aPosAry
.mnSrcHeight
&& aPosAry
.mnDestWidth
&& aPosAry
.mnDestHeight
)
159 if ( nAction
== MetaActionType::BMPSCALE
)
160 ScaleBitmap (aBmp
, aPosAry
);
162 mpGraphics
->DrawBitmap( aPosAry
, *aBmp
.ImplGetSalBitmap(), this );
169 // #i32109#: Make bitmap area opaque
170 mpAlphaVDev
->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt
, rDestSize
) );
174 Bitmap
OutputDevice::GetDownsampledBitmap( const Size
& rDstSz
,
175 const Point
& rSrcPt
, const Size
& rSrcSz
,
176 const Bitmap
& rBmp
, long nMaxBmpDPIX
, long nMaxBmpDPIY
)
180 if( !aBmp
.IsEmpty() )
182 const tools::Rectangle
aBmpRect( Point(), aBmp
.GetSizePixel() );
183 tools::Rectangle
aSrcRect( rSrcPt
, rSrcSz
);
185 // do cropping if necessary
186 if( aSrcRect
.Intersection( aBmpRect
) != aBmpRect
)
188 if( !aSrcRect
.IsEmpty() )
189 aBmp
.Crop( aSrcRect
);
194 if( !aBmp
.IsEmpty() )
196 // do downsampling if necessary
197 Size
aDstSizeTwip( PixelToLogic(LogicToPixel(rDstSz
), MapMode(MapUnit::MapTwip
)) );
199 // #103209# Normalize size (mirroring has to happen outside of this method)
200 aDstSizeTwip
= Size( labs(aDstSizeTwip
.Width()), labs(aDstSizeTwip
.Height()) );
202 const Size
aBmpSize( aBmp
.GetSizePixel() );
203 const double fBmpPixelX
= aBmpSize
.Width();
204 const double fBmpPixelY
= aBmpSize
.Height();
205 const double fMaxPixelX
= aDstSizeTwip
.Width() * nMaxBmpDPIX
/ 1440.0;
206 const double fMaxPixelY
= aDstSizeTwip
.Height() * nMaxBmpDPIY
/ 1440.0;
208 // check, if the bitmap DPI exceeds the maximum DPI (allow 4 pixel rounding tolerance)
209 if( ( ( fBmpPixelX
> ( fMaxPixelX
+ 4 ) ) ||
210 ( fBmpPixelY
> ( fMaxPixelY
+ 4 ) ) ) &&
211 ( fBmpPixelY
> 0.0 ) && ( fMaxPixelY
> 0.0 ) )
215 const double fBmpWH
= fBmpPixelX
/ fBmpPixelY
;
216 const double fMaxWH
= fMaxPixelX
/ fMaxPixelY
;
218 if( fBmpWH
< fMaxWH
)
220 aNewBmpSize
.setWidth( FRound( fMaxPixelY
* fBmpWH
) );
221 aNewBmpSize
.setHeight( FRound( fMaxPixelY
) );
223 else if( fBmpWH
> 0.0 )
225 aNewBmpSize
.setWidth( FRound( fMaxPixelX
) );
226 aNewBmpSize
.setHeight( FRound( fMaxPixelX
/ fBmpWH
) );
229 if( aNewBmpSize
.Width() && aNewBmpSize
.Height() )
230 aBmp
.Scale( aNewBmpSize
);
240 void OutputDevice::DrawBitmapEx( const Point
& rDestPt
,
241 const BitmapEx
& rBitmapEx
)
243 assert(!is_double_buffered_window());
245 if( ImplIsRecordLayout() )
248 if( TransparentType::NONE
== rBitmapEx
.GetTransparentType() )
250 DrawBitmap( rDestPt
, rBitmapEx
.GetBitmap() );
254 const Size
aSizePix( rBitmapEx
.GetSizePixel() );
255 DrawBitmapEx( rDestPt
, PixelToLogic( aSizePix
), Point(), aSizePix
, rBitmapEx
, MetaActionType::BMPEX
);
259 void OutputDevice::DrawBitmapEx( const Point
& rDestPt
, const Size
& rDestSize
,
260 const BitmapEx
& rBitmapEx
)
262 assert(!is_double_buffered_window());
264 if( ImplIsRecordLayout() )
267 if ( TransparentType::NONE
== rBitmapEx
.GetTransparentType() )
269 DrawBitmap( rDestPt
, rDestSize
, rBitmapEx
.GetBitmap() );
273 DrawBitmapEx( rDestPt
, rDestSize
, Point(), rBitmapEx
.GetSizePixel(), rBitmapEx
, MetaActionType::BMPEXSCALE
);
278 void OutputDevice::DrawBitmapEx( const Point
& rDestPt
, const Size
& rDestSize
,
279 const Point
& rSrcPtPixel
, const Size
& rSrcSizePixel
,
280 const BitmapEx
& rBitmapEx
, const MetaActionType nAction
)
282 assert(!is_double_buffered_window());
284 if( ImplIsRecordLayout() )
287 if( TransparentType::NONE
== rBitmapEx
.GetTransparentType() )
289 DrawBitmap( rDestPt
, rDestSize
, rSrcPtPixel
, rSrcSizePixel
, rBitmapEx
.GetBitmap() );
293 if ( RasterOp::Invert
== meRasterOp
)
295 DrawRect( tools::Rectangle( rDestPt
, rDestSize
) );
299 BitmapEx
aBmpEx( rBitmapEx
);
301 if ( mnDrawMode
& ( DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
|
302 DrawModeFlags::GrayBitmap
) )
304 if ( mnDrawMode
& ( DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
) )
306 Bitmap
aColorBmp( aBmpEx
.GetSizePixel(), 1 );
309 if ( mnDrawMode
& DrawModeFlags::BlackBitmap
)
314 aColorBmp
.Erase( Color( cCmpVal
, cCmpVal
, cCmpVal
) );
316 if( aBmpEx
.IsAlpha() )
318 // Create one-bit mask out of alpha channel, by
319 // thresholding it at alpha=0.5. As
320 // DRAWMODE_BLACK/WHITEBITMAP requires monochrome
321 // output, having alpha-induced grey levels is not
323 BitmapEx
aMaskEx(aBmpEx
.GetAlpha().GetBitmap());
324 BitmapFilter::Filter(aMaskEx
, BitmapMonochromeFilter(129));
325 aBmpEx
= BitmapEx(aColorBmp
, aMaskEx
.GetBitmap());
329 aBmpEx
= BitmapEx( aColorBmp
, aBmpEx
.GetMask() );
334 if ( mnDrawMode
& DrawModeFlags::GrayBitmap
)
335 aBmpEx
.Convert( BmpConversion::N8BitGreys
);
343 case MetaActionType::BMPEX
:
344 mpMetaFile
->AddAction( new MetaBmpExAction( rDestPt
, aBmpEx
) );
347 case MetaActionType::BMPEXSCALE
:
348 mpMetaFile
->AddAction( new MetaBmpExScaleAction( rDestPt
, rDestSize
, aBmpEx
) );
351 case MetaActionType::BMPEXSCALEPART
:
352 mpMetaFile
->AddAction( new MetaBmpExScalePartAction( rDestPt
, rDestSize
,
353 rSrcPtPixel
, rSrcSizePixel
, aBmpEx
) );
360 if ( !IsDeviceOutputNecessary() )
363 if ( !mpGraphics
&& !AcquireGraphics() )
366 if ( mbInitClipRegion
)
369 if ( mbOutputClipped
)
372 DrawDeviceBitmap( rDestPt
, rDestSize
, rSrcPtPixel
, rSrcSizePixel
, aBmpEx
);
376 Bitmap
OutputDevice::GetBitmap( const Point
& rSrcPt
, const Size
& rSize
) const
379 long nX
= ImplLogicXToDevicePixel( rSrcPt
.X() );
380 long nY
= ImplLogicYToDevicePixel( rSrcPt
.Y() );
381 long nWidth
= ImplLogicWidthToDevicePixel( rSize
.Width() );
382 long nHeight
= ImplLogicHeightToDevicePixel( rSize
.Height() );
384 if ( mpGraphics
|| AcquireGraphics() )
386 if ( nWidth
> 0 && nHeight
> 0 && nX
<= (mnOutWidth
+ mnOutOffX
) && nY
<= (mnOutHeight
+ mnOutOffY
))
388 tools::Rectangle
aRect( Point( nX
, nY
), Size( nWidth
, nHeight
) );
389 bool bClipped
= false;
391 // X-Coordinate outside of draw area?
392 if ( nX
< mnOutOffX
)
394 nWidth
-= ( mnOutOffX
- nX
);
399 // Y-Coordinate outside of draw area?
400 if ( nY
< mnOutOffY
)
402 nHeight
-= ( mnOutOffY
- nY
);
407 // Width outside of draw area?
408 if ( (nWidth
+ nX
) > (mnOutWidth
+ mnOutOffX
) )
410 nWidth
= mnOutOffX
+ mnOutWidth
- nX
;
414 // Height outside of draw area?
415 if ( (nHeight
+ nY
) > (mnOutHeight
+ mnOutOffY
) )
417 nHeight
= mnOutOffY
+ mnOutHeight
- nY
;
423 // If the visible part has been clipped, we have to create a
424 // Bitmap with the correct size in which we copy the clipped
425 // Bitmap to the correct position.
426 ScopedVclPtrInstance
< VirtualDevice
> aVDev( *this );
428 if ( aVDev
->SetOutputSizePixel( aRect
.GetSize() ) )
430 if ( aVDev
->mpGraphics
|| aVDev
->AcquireGraphics() )
432 if ( (nWidth
> 0) && (nHeight
> 0) )
434 SalTwoRect
aPosAry(nX
, nY
, nWidth
, nHeight
,
435 (aRect
.Left() < mnOutOffX
) ? (mnOutOffX
- aRect
.Left()) : 0L,
436 (aRect
.Top() < mnOutOffY
) ? (mnOutOffY
- aRect
.Top()) : 0L,
438 aVDev
->mpGraphics
->CopyBits( aPosAry
, mpGraphics
, this, this );
442 OSL_ENSURE(false, "CopyBits with zero or negative width or height");
445 aBmp
= aVDev
->GetBitmap( Point(), aVDev
->GetOutputSizePixel() );
456 std::shared_ptr
<SalBitmap
> pSalBmp
= mpGraphics
->GetBitmap( nX
, nY
, nWidth
, nHeight
, this );
460 aBmp
.ImplSetSalBitmap(pSalBmp
);
469 BitmapEx
OutputDevice::GetBitmapEx( const Point
& rSrcPt
, const Size
& rSize
) const
472 // #110958# Extract alpha value from VDev, if any
475 Bitmap
aAlphaBitmap( mpAlphaVDev
->GetBitmap( rSrcPt
, rSize
) );
477 // ensure 8 bit alpha
478 if( aAlphaBitmap
.GetBitCount() > 8 )
479 aAlphaBitmap
.Convert( BmpConversion::N8BitGreys
);
481 return BitmapEx(GetBitmap( rSrcPt
, rSize
), AlphaMask( aAlphaBitmap
) );
484 return BitmapEx(GetBitmap( rSrcPt
, rSize
));
487 void OutputDevice::DrawDeviceBitmap( const Point
& rDestPt
, const Size
& rDestSize
,
488 const Point
& rSrcPtPixel
, const Size
& rSrcSizePixel
,
489 BitmapEx
& rBitmapEx
)
491 assert(!is_double_buffered_window());
493 if (rBitmapEx
.IsAlpha())
495 DrawDeviceAlphaBitmap(rBitmapEx
.GetBitmap(), rBitmapEx
.GetAlpha(), rDestPt
, rDestSize
, rSrcPtPixel
, rSrcSizePixel
);
497 else if (!!rBitmapEx
)
499 SalTwoRect
aPosAry(rSrcPtPixel
.X(), rSrcPtPixel
.Y(), rSrcSizePixel
.Width(), rSrcSizePixel
.Height(),
500 ImplLogicXToDevicePixel(rDestPt
.X()), ImplLogicYToDevicePixel(rDestPt
.Y()),
501 ImplLogicWidthToDevicePixel(rDestSize
.Width()),
502 ImplLogicHeightToDevicePixel(rDestSize
.Height()));
504 const BmpMirrorFlags nMirrFlags
= AdjustTwoRect(aPosAry
, rBitmapEx
.GetSizePixel());
506 if (aPosAry
.mnSrcWidth
&& aPosAry
.mnSrcHeight
&& aPosAry
.mnDestWidth
&& aPosAry
.mnDestHeight
)
509 if (nMirrFlags
!= BmpMirrorFlags::NONE
)
510 rBitmapEx
.Mirror(nMirrFlags
);
512 const SalBitmap
* pSalSrcBmp
= rBitmapEx
.ImplGetBitmapSalBitmap().get();
513 std::shared_ptr
<SalBitmap
> xMaskBmp
= rBitmapEx
.ImplGetMaskSalBitmap();
517 bool bTryDirectPaint(pSalSrcBmp
);
519 if (bTryDirectPaint
&& mpGraphics
->DrawAlphaBitmap(aPosAry
, *pSalSrcBmp
, *xMaskBmp
, this))
521 // tried to paint as alpha directly. If tis worked, we are done (except
526 // #4919452# reduce operation area to bounds of
527 // cliprect. since masked transparency involves
528 // creation of a large vdev and copying the screen
529 // content into that (slooow read from framebuffer),
530 // that should considerably increase performance for
531 // large bitmaps and small clippings.
533 // Note that this optimization is a workaround for a
534 // Writer peculiarity, namely, to decompose background
535 // graphics into myriads of disjunct, tiny
536 // rectangles. That otherwise kills us here, since for
537 // transparent output, SAL always prepares the whole
538 // bitmap, if aPosAry contains the whole bitmap (and
539 // it's _not_ to blame for that).
541 // Note the call to ImplPixelToDevicePixel(), since
542 // aPosAry already contains the mnOutOff-offsets, they
543 // also have to be applied to the region
544 tools::Rectangle
aClipRegionBounds( ImplPixelToDevicePixel(maRegion
).GetBoundRect() );
546 // TODO: Also respect scaling (that's a bit tricky,
547 // since the source points have to move fractional
548 // amounts (which is not possible, thus has to be
549 // emulated by increases copy area)
550 // const double nScaleX( aPosAry.mnDestWidth / aPosAry.mnSrcWidth );
551 // const double nScaleY( aPosAry.mnDestHeight / aPosAry.mnSrcHeight );
553 // for now, only identity scales allowed
554 if (!aClipRegionBounds
.IsEmpty() &&
555 aPosAry
.mnDestWidth
== aPosAry
.mnSrcWidth
&&
556 aPosAry
.mnDestHeight
== aPosAry
.mnSrcHeight
)
558 // now intersect dest rect with clip region
559 aClipRegionBounds
.Intersection(tools::Rectangle(aPosAry
.mnDestX
,
561 aPosAry
.mnDestX
+ aPosAry
.mnDestWidth
- 1,
562 aPosAry
.mnDestY
+ aPosAry
.mnDestHeight
- 1));
564 // Note: I could theoretically optimize away the
565 // DrawBitmap below, if the region is empty
566 // here. Unfortunately, cannot rule out that
567 // somebody relies on the side effects.
568 if (!aClipRegionBounds
.IsEmpty())
570 aPosAry
.mnSrcX
+= aClipRegionBounds
.Left() - aPosAry
.mnDestX
;
571 aPosAry
.mnSrcY
+= aClipRegionBounds
.Top() - aPosAry
.mnDestY
;
572 aPosAry
.mnSrcWidth
= aClipRegionBounds
.GetWidth();
573 aPosAry
.mnSrcHeight
= aClipRegionBounds
.GetHeight();
575 aPosAry
.mnDestX
= aClipRegionBounds
.Left();
576 aPosAry
.mnDestY
= aClipRegionBounds
.Top();
577 aPosAry
.mnDestWidth
= aClipRegionBounds
.GetWidth();
578 aPosAry
.mnDestHeight
= aClipRegionBounds
.GetHeight();
582 mpGraphics
->DrawBitmap(aPosAry
, *pSalSrcBmp
, *xMaskBmp
, this);
585 // #110958# Paint mask to alpha channel. Luckily, the
586 // black and white representation of the mask maps to
589 // #i25167# Restrict mask painting to _opaque_ areas
590 // of the mask, otherwise we spoil areas where no
591 // bitmap content was ever visible. Interestingly
592 // enough, this can be achieved by taking the mask as
593 // the transparency mask of itself
595 mpAlphaVDev
->DrawBitmapEx(rDestPt
,
597 BitmapEx(rBitmapEx
.GetMask(),
598 rBitmapEx
.GetMask()));
602 mpGraphics
->DrawBitmap(aPosAry
, *pSalSrcBmp
, this);
606 // #i32109#: Make bitmap area opaque
607 mpAlphaVDev
->ImplFillOpaqueRectangle( tools::Rectangle(rDestPt
, rDestSize
) );
614 void OutputDevice::DrawDeviceAlphaBitmap( const Bitmap
& rBmp
, const AlphaMask
& rAlpha
,
615 const Point
& rDestPt
, const Size
& rDestSize
,
616 const Point
& rSrcPtPixel
, const Size
& rSrcSizePixel
)
618 assert(!is_double_buffered_window());
620 Point
aOutPt(LogicToPixel(rDestPt
));
621 Size
aOutSz(LogicToPixel(rDestSize
));
622 tools::Rectangle
aDstRect(Point(), GetOutputSizePixel());
624 const bool bHMirr
= aOutSz
.Width() < 0;
625 const bool bVMirr
= aOutSz
.Height() < 0;
627 ClipToPaintRegion(aDstRect
);
631 aOutSz
.setWidth( -aOutSz
.Width() );
632 aOutPt
.AdjustX( -(aOutSz
.Width() - 1) );
637 aOutSz
.setHeight( -aOutSz
.Height() );
638 aOutPt
.AdjustY( -(aOutSz
.Height() - 1) );
641 if (!aDstRect
.Intersection(tools::Rectangle(aOutPt
, aOutSz
)).IsEmpty())
643 static const char* pDisableNative
= getenv( "SAL_DISABLE_NATIVE_ALPHA");
644 // #i83087# Naturally, system alpha blending cannot work with
645 // separate alpha VDev
647 // Not clear how the above comment relates to the following declaration and initialisation
648 // of bTryDirectPaint. Does bTryDirectPaint being true mean that we can use "system alpha
649 // blending"? Or that we can't? Or are the two not related at all, and should the above
650 // comment actually be better located below, before the "if (mpAlphaVDev)" test?
652 bool bTryDirectPaint(!pDisableNative
&& !bHMirr
&& !bVMirr
);
656 Point aRelPt
= aOutPt
+ Point(mnOutOffX
, mnOutOffY
);
658 rSrcPtPixel
.X(), rSrcPtPixel
.Y(),
659 rSrcSizePixel
.Width(), rSrcSizePixel
.Height(),
660 aRelPt
.X(), aRelPt
.Y(),
661 aOutSz
.Width(), aOutSz
.Height());
663 SalBitmap
* pSalSrcBmp
= rBmp
.ImplGetSalBitmap().get();
664 SalBitmap
* pSalAlphaBmp
= rAlpha
.ImplGetSalBitmap().get();
666 // try to blend the alpha bitmap with the alpha virtual device
669 Bitmap
aAlphaBitmap( mpAlphaVDev
->GetBitmap( aRelPt
, aOutSz
) );
670 if (aAlphaBitmap
.ImplGetSalBitmap())
672 SalBitmap
* pSalAlphaBmp2
= aAlphaBitmap
.ImplGetSalBitmap().get();
673 if (mpGraphics
->BlendAlphaBitmap(aTR
, *pSalSrcBmp
, *pSalAlphaBmp
, *pSalAlphaBmp2
, this))
675 mpAlphaVDev
->BlendBitmap(aTR
, rAlpha
);
682 if (mpGraphics
->DrawAlphaBitmap(aTR
, *pSalSrcBmp
, *pSalAlphaBmp
, this))
687 // we need to make sure OpenGL never reaches this slow code path
689 #if HAVE_FEATURE_OPENGL
690 assert(!OpenGLHelper::isVCLOpenGLEnabled());
692 tools::Rectangle
aBmpRect(Point(), rBmp
.GetSizePixel());
693 if (!aBmpRect
.Intersection(tools::Rectangle(rSrcPtPixel
, rSrcSizePixel
)).IsEmpty())
695 Point
auxOutPt(LogicToPixel(rDestPt
));
696 Size
auxOutSz(LogicToPixel(rDestSize
));
698 DrawDeviceAlphaBitmapSlowPath(rBmp
, rAlpha
, aDstRect
, aBmpRect
, auxOutSz
, auxOutPt
);
706 struct LinearScaleContext
708 std::unique_ptr
<long[]> mpMapX
;
709 std::unique_ptr
<long[]> mpMapY
;
711 std::unique_ptr
<long[]> mpMapXOffset
;
712 std::unique_ptr
<long[]> mpMapYOffset
;
714 LinearScaleContext(tools::Rectangle
const & aDstRect
, tools::Rectangle
const & aBitmapRect
,
715 Size
const & aOutSize
, long nOffX
, long nOffY
)
717 : mpMapX(new long[aDstRect
.GetWidth()])
718 , mpMapY(new long[aDstRect
.GetHeight()])
719 , mpMapXOffset(new long[aDstRect
.GetWidth()])
720 , mpMapYOffset(new long[aDstRect
.GetHeight()])
722 const long nSrcWidth
= aBitmapRect
.GetWidth();
723 const long nSrcHeight
= aBitmapRect
.GetHeight();
726 nSrcWidth
, aDstRect
.GetWidth(), aBitmapRect
.Left(),
727 aOutSize
.Width(), nOffX
, mpMapX
.get(), mpMapXOffset
.get());
730 nSrcHeight
, aDstRect
.GetHeight(), aBitmapRect
.Top(),
731 aOutSize
.Height(), nOffY
, mpMapY
.get(), mpMapYOffset
.get());
736 static void generateSimpleMap(long nSrcDimension
, long nDstDimension
, long nDstLocation
,
737 long nOutDimention
, long nOffset
, long* pMap
, long* pMapOffset
)
740 const double fReverseScale
= (std::abs(nOutDimention
) > 1) ? (nSrcDimension
- 1) / double(std::abs(nOutDimention
) - 1) : 0.0;
742 long nSampleRange
= std::max(0L, nSrcDimension
- 2);
744 for (long i
= 0; i
< nDstDimension
; i
++)
746 double fTemp
= std::abs((nOffset
+ i
) * fReverseScale
);
748 pMap
[i
] = MinMax(nDstLocation
+ long(fTemp
), 0, nSampleRange
);
749 pMapOffset
[i
] = static_cast<long>((fTemp
- pMap
[i
]) * 128.0);
755 const BitmapWriteAccess
* pDestination
,
756 const BitmapReadAccess
* pSource
,
757 const BitmapReadAccess
* pSourceAlpha
,
758 const long nDstWidth
,
759 const long nDstHeight
)
761 if (pSource
&& pSourceAlpha
&& pDestination
)
763 ScanlineFormat nSourceFormat
= pSource
->GetScanlineFormat();
764 ScanlineFormat nDestinationFormat
= pDestination
->GetScanlineFormat();
766 switch (nSourceFormat
)
768 case ScanlineFormat::N24BitTcRgb
:
769 case ScanlineFormat::N24BitTcBgr
:
771 if ( (nSourceFormat
== ScanlineFormat::N24BitTcBgr
&& nDestinationFormat
== ScanlineFormat::N32BitTcBgra
)
772 || (nSourceFormat
== ScanlineFormat::N24BitTcRgb
&& nDestinationFormat
== ScanlineFormat::N32BitTcRgba
))
774 blendBitmap24(pDestination
, pSource
, pSourceAlpha
, nDstWidth
, nDstHeight
);
786 const BitmapWriteAccess
* pDestination
,
787 const BitmapReadAccess
* pSource
,
788 const BitmapReadAccess
* pSourceAlpha
,
789 const long nDstWidth
,
790 const long nDstHeight
)
792 Scanline pLine0
, pLine1
;
793 Scanline pLineAlpha0
, pLineAlpha1
;
794 Scanline pColorSample1
, pColorSample2
;
795 Scanline pDestScanline
;
797 long nColor1Line1
, nColor2Line1
, nColor3Line1
;
798 long nColor1Line2
, nColor2Line2
, nColor3Line2
;
799 long nAlphaLine1
, nAlphaLine2
;
801 sal_uInt8 nColor1
, nColor2
, nColor3
, nAlpha
;
803 for (long nY
= 0; nY
< nDstHeight
; nY
++)
805 const long nMapY
= mpMapY
[nY
];
806 const long nMapFY
= mpMapYOffset
[nY
];
808 pLine0
= pSource
->GetScanline(nMapY
);
809 // tdf#95481 guard nMapY + 1 to be within bounds
810 pLine1
= (nMapY
+ 1 < pSource
->Height()) ? pSource
->GetScanline(nMapY
+ 1) : pLine0
;
812 pLineAlpha0
= pSourceAlpha
->GetScanline(nMapY
);
813 // tdf#95481 guard nMapY + 1 to be within bounds
814 pLineAlpha1
= (nMapY
+ 1 < pSourceAlpha
->Height()) ? pSourceAlpha
->GetScanline(nMapY
+ 1) : pLineAlpha0
;
816 pDestScanline
= pDestination
->GetScanline(nY
);
818 for (long nX
= 0; nX
< nDstWidth
; nX
++)
820 const long nMapX
= mpMapX
[nX
];
821 const long nMapFX
= mpMapXOffset
[nX
];
823 pColorSample1
= pLine0
+ 3 * nMapX
;
824 pColorSample2
= (nMapX
+ 1 < pSource
->Width()) ? pColorSample1
+ 3 : pColorSample1
;
825 nColor1Line1
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
829 nColor2Line1
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
833 nColor3Line1
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
835 pColorSample1
= pLine1
+ 3 * nMapX
;
836 pColorSample2
= (nMapX
+ 1 < pSource
->Width()) ? pColorSample1
+ 3 : pColorSample1
;
837 nColor1Line2
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
841 nColor2Line2
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
845 nColor3Line2
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
847 pColorSample1
= pLineAlpha0
+ nMapX
;
848 pColorSample2
= (nMapX
+ 1 < pSourceAlpha
->Width()) ? pColorSample1
+ 1 : pColorSample1
;
849 nAlphaLine1
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
851 pColorSample1
= pLineAlpha1
+ nMapX
;
852 pColorSample2
= (nMapX
+ 1 < pSourceAlpha
->Width()) ? pColorSample1
+ 1 : pColorSample1
;
853 nAlphaLine2
= (static_cast<long>(*pColorSample1
) << 7) + nMapFX
* (static_cast<long>(*pColorSample2
) - *pColorSample1
);
855 nColor1
= (nColor1Line1
+ nMapFY
* ((nColor1Line2
>> 7) - (nColor1Line1
>> 7))) >> 7;
856 nColor2
= (nColor2Line1
+ nMapFY
* ((nColor2Line2
>> 7) - (nColor2Line1
>> 7))) >> 7;
857 nColor3
= (nColor3Line1
+ nMapFY
* ((nColor3Line2
>> 7) - (nColor3Line1
>> 7))) >> 7;
859 nAlpha
= (nAlphaLine1
+ nMapFY
* ((nAlphaLine2
>> 7) - (nAlphaLine1
>> 7))) >> 7;
861 *pDestScanline
= ColorChannelMerge(*pDestScanline
, nColor1
, nAlpha
);
863 *pDestScanline
= ColorChannelMerge(*pDestScanline
, nColor2
, nAlpha
);
865 *pDestScanline
= ColorChannelMerge(*pDestScanline
, nColor3
, nAlpha
);
873 struct TradScaleContext
875 std::unique_ptr
<long[]> mpMapX
;
876 std::unique_ptr
<long[]> mpMapY
;
878 TradScaleContext(tools::Rectangle
const & aDstRect
, tools::Rectangle
const & aBitmapRect
,
879 Size
const & aOutSize
, long nOffX
, long nOffY
)
881 : mpMapX(new long[aDstRect
.GetWidth()])
882 , mpMapY(new long[aDstRect
.GetHeight()])
884 const long nSrcWidth
= aBitmapRect
.GetWidth();
885 const long nSrcHeight
= aBitmapRect
.GetHeight();
887 const bool bHMirr
= aOutSize
.Width() < 0;
888 const bool bVMirr
= aOutSize
.Height() < 0;
891 nSrcWidth
, aDstRect
.GetWidth(), aBitmapRect
.Left(),
892 aOutSize
.Width(), nOffX
, bHMirr
, mpMapX
.get());
895 nSrcHeight
, aDstRect
.GetHeight(), aBitmapRect
.Top(),
896 aOutSize
.Height(), nOffY
, bVMirr
, mpMapY
.get());
901 static void generateSimpleMap(long nSrcDimension
, long nDstDimension
, long nDstLocation
,
902 long nOutDimention
, long nOffset
, bool bMirror
, long* pMap
)
904 long nMirrorOffset
= 0;
907 nMirrorOffset
= (nDstLocation
<< 1) + nSrcDimension
- 1;
909 for (long i
= 0; i
< nDstDimension
; ++i
, ++nOffset
)
911 pMap
[i
] = nDstLocation
+ nOffset
* nSrcDimension
/ nOutDimention
;
913 pMap
[i
] = nMirrorOffset
- pMap
[i
];
919 } // end anonymous namespace
921 void OutputDevice::DrawDeviceAlphaBitmapSlowPath(const Bitmap
& rBitmap
,
922 const AlphaMask
& rAlpha
, tools::Rectangle aDstRect
, tools::Rectangle aBmpRect
, Size
const & aOutSize
, Point
const & aOutPoint
)
924 assert(!is_double_buffered_window());
926 VirtualDevice
* pOldVDev
= mpAlphaVDev
;
928 const bool bHMirr
= aOutSize
.Width() < 0;
929 const bool bVMirr
= aOutSize
.Height() < 0;
931 // The scaling in this code path produces really ugly results - it
932 // does the most trivial scaling with no smoothing.
933 GDIMetaFile
* pOldMetaFile
= mpMetaFile
;
934 const bool bOldMap
= mbMap
;
936 mpMetaFile
= nullptr; // fdo#55044 reset before GetBitmap!
939 Bitmap
aBmp(GetBitmap(aDstRect
.TopLeft(), aDstRect
.GetSize()));
941 // #109044# The generated bitmap need not necessarily be
942 // of aDstRect dimensions, it's internally clipped to
943 // window bounds. Thus, we correct the dest size here,
944 // since we later use it (in nDstWidth/Height) for pixel
946 // #i38887# reading from screen may sometimes fail
947 if (aBmp
.ImplGetSalBitmap())
949 aDstRect
.SetSize(aBmp
.GetSizePixel());
952 const long nDstWidth
= aDstRect
.GetWidth();
953 const long nDstHeight
= aDstRect
.GetHeight();
955 // calculate offset in original bitmap
956 // in RTL case this is a little more complicated since the contents of the
957 // bitmap is not mirrored (it never is), however the paint region and bmp region
958 // are in mirrored coordinates, so the intersection of (aOutPt,aOutSz) with these
959 // is content wise somewhere else and needs to take mirroring into account
960 const long nOffX
= IsRTLEnabled()
961 ? aOutSize
.Width() - aDstRect
.GetWidth() - (aDstRect
.Left() - aOutPoint
.X())
962 : aDstRect
.Left() - aOutPoint
.X();
964 const long nOffY
= aDstRect
.Top() - aOutPoint
.Y();
966 TradScaleContext
aTradContext(aDstRect
, aBmpRect
, aOutSize
, nOffX
, nOffY
);
968 Bitmap::ScopedReadAccess
pBitmapReadAccess(const_cast<Bitmap
&>(rBitmap
));
969 AlphaMask::ScopedReadAccess
pAlphaReadAccess(const_cast<AlphaMask
&>(rAlpha
));
971 DBG_ASSERT( pAlphaReadAccess
->GetScanlineFormat() == ScanlineFormat::N8BitPal
||
972 pAlphaReadAccess
->GetScanlineFormat() == ScanlineFormat::N8BitTcMask
,
973 "OutputDevice::ImplDrawAlpha(): non-8bit alpha no longer supported!" );
975 // #i38887# reading from screen may sometimes fail
976 if (aBmp
.ImplGetSalBitmap())
982 aNewBitmap
= BlendBitmapWithAlpha(
983 aBmp
, pBitmapReadAccess
.get(), pAlphaReadAccess
.get(),
987 aTradContext
.mpMapX
.get(), aTradContext
.mpMapY
.get() );
991 LinearScaleContext
aLinearContext(aDstRect
, aBmpRect
, aOutSize
, nOffX
, nOffY
);
993 if (aLinearContext
.blendBitmap( BitmapScopedWriteAccess(aBmp
).get(), pBitmapReadAccess
.get(), pAlphaReadAccess
.get(),
994 nDstWidth
, nDstHeight
))
1000 aNewBitmap
= BlendBitmap(
1001 aBmp
, pBitmapReadAccess
.get(), pAlphaReadAccess
.get(),
1006 aTradContext
.mpMapX
.get(), aTradContext
.mpMapY
.get() );
1010 // #110958# Disable alpha VDev, we're doing the necessary
1011 // stuff explicitly further below
1013 mpAlphaVDev
= nullptr;
1015 DrawBitmap(aDstRect
.TopLeft(), aNewBitmap
);
1017 // #110958# Enable alpha VDev again
1018 mpAlphaVDev
= pOldVDev
;
1022 mpMetaFile
= pOldMetaFile
;
1025 void OutputDevice::ScaleBitmap (Bitmap
&rBmp
, SalTwoRect
&rPosAry
)
1027 const double nScaleX
= rPosAry
.mnDestWidth
/ static_cast<double>( rPosAry
.mnSrcWidth
);
1028 const double nScaleY
= rPosAry
.mnDestHeight
/ static_cast<double>( rPosAry
.mnSrcHeight
);
1030 // If subsampling, use Bitmap::Scale for subsampling for better quality.
1031 if ( nScaleX
< 1.0 || nScaleY
< 1.0 )
1033 rBmp
.Scale ( nScaleX
, nScaleY
);
1034 rPosAry
.mnSrcWidth
= rPosAry
.mnDestWidth
;
1035 rPosAry
.mnSrcHeight
= rPosAry
.mnDestHeight
;
1039 bool OutputDevice::DrawTransformBitmapExDirect(
1040 const basegfx::B2DHomMatrix
& aFullTransform
,
1041 const BitmapEx
& rBitmapEx
)
1043 assert(!is_double_buffered_window());
1047 // try to paint directly
1048 const basegfx::B2DPoint
aNull(aFullTransform
* basegfx::B2DPoint(0.0, 0.0));
1049 const basegfx::B2DPoint
aTopX(aFullTransform
* basegfx::B2DPoint(1.0, 0.0));
1050 const basegfx::B2DPoint
aTopY(aFullTransform
* basegfx::B2DPoint(0.0, 1.0));
1051 SalBitmap
* pSalSrcBmp
= rBitmapEx
.GetBitmap().ImplGetSalBitmap().get();
1052 SalBitmap
* pSalAlphaBmp
= nullptr;
1054 if(rBitmapEx
.IsTransparent())
1056 if(rBitmapEx
.IsAlpha())
1058 pSalAlphaBmp
= rBitmapEx
.GetAlpha().ImplGetSalBitmap().get();
1062 pSalAlphaBmp
= rBitmapEx
.GetMask().ImplGetSalBitmap().get();
1066 bDone
= mpGraphics
->DrawTransformedBitmap(
1077 bool OutputDevice::TransformAndReduceBitmapExToTargetRange(
1078 const basegfx::B2DHomMatrix
& aFullTransform
,
1079 basegfx::B2DRange
&aVisibleRange
,
1080 double &fMaximumArea
)
1082 // limit TargetRange to existing pixels (if pixel device)
1083 // first get discrete range of object
1084 basegfx::B2DRange
aFullPixelRange(aVisibleRange
);
1086 aFullPixelRange
.transform(aFullTransform
);
1088 if(basegfx::fTools::equalZero(aFullPixelRange
.getWidth()) || basegfx::fTools::equalZero(aFullPixelRange
.getHeight()))
1090 // object is outside of visible area
1094 // now get discrete target pixels; start with OutDev pixel size and evtl.
1095 // intersect with active clipping area
1096 basegfx::B2DRange
aOutPixel(
1099 GetOutputSizePixel().Width(),
1100 GetOutputSizePixel().Height());
1104 tools::Rectangle
aRegionRectangle(GetActiveClipRegion().GetBoundRect());
1106 // caution! Range from rectangle, one too much (!)
1107 aRegionRectangle
.AdjustRight(-1);
1108 aRegionRectangle
.AdjustBottom(-1);
1109 aOutPixel
.intersect( vcl::unotools::b2DRectangleFromRectangle(aRegionRectangle
) );
1112 if(aOutPixel
.isEmpty())
1114 // no active output area
1118 // if aFullPixelRange is not completely inside of aOutPixel,
1119 // reduction of target pixels is possible
1120 basegfx::B2DRange
aVisiblePixelRange(aFullPixelRange
);
1122 if(!aOutPixel
.isInside(aFullPixelRange
))
1124 aVisiblePixelRange
.intersect(aOutPixel
);
1126 if(aVisiblePixelRange
.isEmpty())
1128 // nothing in visible part, reduces to nothing
1132 // aVisiblePixelRange contains the reduced output area in
1133 // discrete coordinates. To make it useful everywhere, make it relative to
1135 basegfx::B2DHomMatrix aMakeVisibleRangeRelative
;
1137 aVisibleRange
= aVisiblePixelRange
;
1138 aMakeVisibleRangeRelative
.translate(
1139 -aFullPixelRange
.getMinX(),
1140 -aFullPixelRange
.getMinY());
1141 aMakeVisibleRangeRelative
.scale(
1142 1.0 / aFullPixelRange
.getWidth(),
1143 1.0 / aFullPixelRange
.getHeight());
1144 aVisibleRange
.transform(aMakeVisibleRangeRelative
);
1147 // for pixel devices, do *not* limit size, else OutputDevice::DrawDeviceAlphaBitmap
1148 // will create another, badly scaled bitmap to do the job. Nonetheless, do a
1149 // maximum clipping of something big (1600x1280x2). Add 1.0 to avoid rounding
1150 // errors in rough estimations
1151 const double fNewMaxArea(aVisiblePixelRange
.getWidth() * aVisiblePixelRange
.getHeight());
1153 fMaximumArea
= std::min(4096000.0, fNewMaxArea
+ 1.0);
1158 void OutputDevice::DrawTransformedBitmapEx(
1159 const basegfx::B2DHomMatrix
& rTransformation
,
1160 const BitmapEx
& rBitmapEx
)
1162 assert(!is_double_buffered_window());
1164 if( ImplIsRecordLayout() )
1167 if(rBitmapEx
.IsEmpty())
1170 // decompose matrix to check rotation and shear
1171 basegfx::B2DVector aScale
, aTranslate
;
1172 double fRotate
, fShearX
;
1173 rTransformation
.decompose(aScale
, aTranslate
, fRotate
, fShearX
);
1174 const bool bRotated(!basegfx::fTools::equalZero(fRotate
));
1175 const bool bSheared(!basegfx::fTools::equalZero(fShearX
));
1176 const bool bMirroredX(basegfx::fTools::less(aScale
.getX(), 0.0));
1177 const bool bMirroredY(basegfx::fTools::less(aScale
.getY(), 0.0));
1179 const bool bMetafile
= mpMetaFile
!= nullptr;
1181 if(!bRotated
&& !bSheared
&& !bMirroredX
&& !bMirroredY
)
1183 // with no rotation, shear or mirroring it can be mapped to DrawBitmapEx
1184 // do *not* execute the mirroring here, it's done in the fallback
1185 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1186 Point
aDestPt(basegfx::fround(aTranslate
.getX()), basegfx::fround(aTranslate
.getY()));
1187 const Size
aDestSize(
1188 basegfx::fround(aScale
.getX() + aTranslate
.getX()) - aDestPt
.X(),
1189 basegfx::fround(aScale
.getY() + aTranslate
.getY()) - aDestPt
.Y());
1190 const Point aOrigin
= GetMapMode().GetOrigin();
1191 if (!bMetafile
&& comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel
)
1193 aDestPt
.Move(aOrigin
.getX(), aOrigin
.getY());
1194 EnableMapMode(false);
1197 DrawBitmapEx(aDestPt
, aDestSize
, rBitmapEx
);
1198 if (!bMetafile
&& comphelper::LibreOfficeKit::isActive() && GetMapMode().GetMapUnit() != MapUnit::MapPixel
)
1201 aDestPt
.Move(-aOrigin
.getX(), -aOrigin
.getY());
1206 // we have rotation,shear or mirror, check if some crazy mode needs the
1207 // created transformed bitmap
1208 const bool bInvert(RasterOp::Invert
== meRasterOp
);
1209 const bool bBitmapChangedColor(mnDrawMode
& (DrawModeFlags::BlackBitmap
| DrawModeFlags::WhiteBitmap
| DrawModeFlags::GrayBitmap
));
1211 basegfx::B2DHomMatrix
aFullTransform(GetViewTransformation() * rTransformation
);
1212 const bool bTryDirectPaint(!bInvert
&& !bBitmapChangedColor
&& !bMetafile
);
1216 bDone
= DrawTransformBitmapExDirect(aFullTransform
, rBitmapEx
);
1221 // take the fallback when no rotate and shear, but mirror (else we would have done this above)
1222 if(!bRotated
&& !bSheared
)
1224 // with no rotation or shear it can be mapped to DrawBitmapEx
1225 // do *not* execute the mirroring here, it's done in the fallback
1226 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1227 const Point
aDestPt(basegfx::fround(aTranslate
.getX()), basegfx::fround(aTranslate
.getY()));
1228 const Size
aDestSize(
1229 basegfx::fround(aScale
.getX() + aTranslate
.getX()) - aDestPt
.X(),
1230 basegfx::fround(aScale
.getY() + aTranslate
.getY()) - aDestPt
.Y());
1232 DrawBitmapEx(aDestPt
, aDestSize
, rBitmapEx
);
1236 assert(bSheared
|| bRotated
); // at this point we are either sheared or rotated or both
1238 // fallback; create transformed bitmap the hard way (back-transform
1239 // the pixels) and paint
1240 basegfx::B2DRange
aVisibleRange(0.0, 0.0, 1.0, 1.0);
1242 // limit maximum area to something looking good for non-pixel-based targets (metafile, printer)
1243 // by using a fixed minimum (allow at least, but no need to utilize) for good smoothing and an area
1244 // dependent of original size for good quality when e.g. rotated/sheared. Still, limit to a maximum
1245 // to avoid crashes/resource problems (ca. 1500x3000 here)
1246 const Size
& rOriginalSizePixel(rBitmapEx
.GetSizePixel());
1247 const double fOrigArea(rOriginalSizePixel
.Width() * rOriginalSizePixel
.Height() * 0.5);
1248 const double fOrigAreaScaled(fOrigArea
* 1.44);
1249 double fMaximumArea(std::min(4500000.0, std::max(1000000.0, fOrigAreaScaled
)));
1253 if ( !TransformAndReduceBitmapExToTargetRange( aFullTransform
, aVisibleRange
, fMaximumArea
) )
1257 if(!aVisibleRange
.isEmpty())
1259 BitmapEx
aTransformed(rBitmapEx
);
1261 // #122923# when the result needs an alpha channel due to being rotated or sheared
1262 // and thus uncovering areas, add these channels so that the own transformer (used
1263 // in getTransformed) also creates a transformed alpha channel
1264 if(!aTransformed
.IsTransparent() && (bSheared
|| bRotated
))
1266 // parts will be uncovered, extend aTransformed with a mask bitmap
1267 const Bitmap
aContent(aTransformed
.GetBitmap());
1269 AlphaMask
aMaskBmp(aContent
.GetSizePixel());
1272 aTransformed
= BitmapEx(aContent
, aMaskBmp
);
1275 // Remove scaling from aFulltransform: we transform due to shearing or rotation, scaling
1276 // will happen according to aDestSize.
1277 basegfx::B2DVector aFullScale
, aFullTranslate
;
1278 double fFullRotate
, fFullShearX
;
1279 aFullTransform
.decompose(aFullScale
, aFullTranslate
, fFullRotate
, fFullShearX
);
1280 // Require positive scaling, negative scaling would loose horizontal or vertical flip.
1281 if (aFullScale
.getX() > 0 && aFullScale
.getY() > 0)
1283 basegfx::B2DHomMatrix aTransform
= basegfx::utils::createScaleB2DHomMatrix(
1284 rOriginalSizePixel
.getWidth() / aFullScale
.getX(),
1285 rOriginalSizePixel
.getHeight() / aFullScale
.getY());
1286 aFullTransform
*= aTransform
;
1289 double fSourceRatio
= 1.0;
1290 if (rOriginalSizePixel
.getHeight() != 0)
1292 fSourceRatio
= rOriginalSizePixel
.getWidth() / rOriginalSizePixel
.getHeight();
1294 double fTargetRatio
= 1.0;
1295 if (aFullScale
.getY() != 0)
1297 fTargetRatio
= aFullScale
.getX() / aFullScale
.getY();
1299 bool bAspectRatioKept
= rtl::math::approxEqual(fSourceRatio
, fTargetRatio
);
1300 if (bSheared
|| !bAspectRatioKept
)
1302 // Not only rotation, or scaling does not keep aspect ratio.
1303 aTransformed
= aTransformed
.getTransformed(
1310 // Just rotation, can do that directly.
1311 fFullRotate
= fmod(fFullRotate
* -1, F_2PI
);
1312 if (fFullRotate
< 0)
1314 fFullRotate
+= F_2PI
;
1316 long nAngle10
= basegfx::fround(basegfx::rad2deg(fFullRotate
) * 10);
1317 aTransformed
.Rotate(nAngle10
, COL_TRANSPARENT
);
1319 basegfx::B2DRange
aTargetRange(0.0, 0.0, 1.0, 1.0);
1321 // get logic object target range
1322 aTargetRange
.transform(rTransformation
);
1324 // get from unified/relative VisibleRange to logoc one
1325 aVisibleRange
.transform(
1326 basegfx::utils::createScaleTranslateB2DHomMatrix(
1327 aTargetRange
.getRange(),
1328 aTargetRange
.getMinimum()));
1330 // extract point and size; do not remove size, the bitmap may have been prepared reduced by purpose
1331 // #i124580# the correct DestSize needs to be calculated based on MaxXY values
1332 const Point
aDestPt(basegfx::fround(aVisibleRange
.getMinX()), basegfx::fround(aVisibleRange
.getMinY()));
1333 const Size
aDestSize(
1334 basegfx::fround(aVisibleRange
.getMaxX()) - aDestPt
.X(),
1335 basegfx::fround(aVisibleRange
.getMaxY()) - aDestPt
.Y());
1337 DrawBitmapEx(aDestPt
, aDestSize
, aTransformed
);
1342 void OutputDevice::DrawShadowBitmapEx(
1343 const BitmapEx
& rBitmapEx
,
1344 ::Color aShadowColor
)
1346 Bitmap::ScopedReadAccess
pReadAccess(const_cast<Bitmap
&>(rBitmapEx
.maBitmap
));
1351 for(long y(0); y
< pReadAccess
->Height(); y
++)
1353 for(long x(0); x
< pReadAccess
->Width(); x
++)
1355 const BitmapColor aColor
= pReadAccess
->GetColor(y
, x
);
1356 sal_uInt16
nLuminance(static_cast<sal_uInt16
>(aColor
.GetLuminance()) + 1);
1357 const Color
aDestColor(
1358 static_cast<sal_uInt8
>((nLuminance
* static_cast<sal_uInt16
>(aShadowColor
.GetRed())) >> 8),
1359 static_cast<sal_uInt8
>((nLuminance
* static_cast<sal_uInt16
>(aShadowColor
.GetGreen())) >> 8),
1360 static_cast<sal_uInt8
>((nLuminance
* static_cast<sal_uInt16
>(aShadowColor
.GetBlue())) >> 8));
1361 DrawPixel(Point(x
,y
), aDestColor
);
1366 void OutputDevice::DrawImage( const Point
& rPos
, const Image
& rImage
, DrawImageFlags nStyle
)
1368 assert(!is_double_buffered_window());
1370 DrawImage( rPos
, Size(), rImage
, nStyle
);
1373 void OutputDevice::DrawImage( const Point
& rPos
, const Size
& rSize
,
1374 const Image
& rImage
, DrawImageFlags nStyle
)
1376 assert(!is_double_buffered_window());
1378 bool bIsSizeValid
= rSize
.getWidth() != 0 && rSize
.getHeight() != 0;
1380 if (!ImplIsRecordLayout())
1382 Image
& rNonConstImage
= const_cast<Image
&>(rImage
);
1384 rNonConstImage
.Draw(this, rPos
, nStyle
, &rSize
);
1386 rNonConstImage
.Draw(this, rPos
, nStyle
);
1392 // Co = Cs + Cd*(1-As) premultiplied alpha -or-
1393 // Co = (AsCs + AdCd*(1-As)) / Ao
1394 sal_uInt8
CalcColor( const sal_uInt8 nSourceColor
, const sal_uInt8 nSourceAlpha
,
1395 const sal_uInt8 nDstAlpha
, const sal_uInt8 nResAlpha
, const sal_uInt8 nDestColor
)
1397 int c
= nResAlpha
? ( static_cast<int>(nSourceAlpha
)*nSourceColor
+ static_cast<int>(nDstAlpha
)*nDestColor
-
1398 static_cast<int>(nDstAlpha
)*nDestColor
*nSourceAlpha
/255 ) / static_cast<int>(nResAlpha
) : 0;
1399 return sal_uInt8( c
);
1402 BitmapColor
AlphaBlend( int nX
, int nY
,
1405 BitmapReadAccess
const * pP
,
1406 BitmapReadAccess
const * pA
,
1407 BitmapReadAccess
const * pB
,
1408 BitmapWriteAccess
const * pAlphaW
,
1409 sal_uInt8
& nResAlpha
)
1411 BitmapColor aDstCol
,aSrcCol
;
1412 aSrcCol
= pP
->GetColor( nMapY
, nMapX
);
1413 aDstCol
= pB
->GetColor( nY
, nX
);
1415 // vcl stores transparency, not alpha - invert it
1416 const sal_uInt8 nSrcAlpha
= 255 - pA
->GetPixelIndex( nMapY
, nMapX
);
1417 const sal_uInt8 nDstAlpha
= 255 - pAlphaW
->GetPixelIndex( nY
, nX
);
1419 // Perform porter-duff compositing 'over' operation
1421 // Co = Cs + Cd*(1-As)
1422 // Ad = As + Ad*(1-As)
1423 nResAlpha
= static_cast<int>(nSrcAlpha
) + static_cast<int>(nDstAlpha
) - static_cast<int>(nDstAlpha
)*nSrcAlpha
/255;
1425 aDstCol
.SetRed( CalcColor( aSrcCol
.GetRed(), nSrcAlpha
, nDstAlpha
, nResAlpha
, aDstCol
.GetRed() ) );
1426 aDstCol
.SetBlue( CalcColor( aSrcCol
.GetBlue(), nSrcAlpha
, nDstAlpha
, nResAlpha
, aDstCol
.GetBlue() ) );
1427 aDstCol
.SetGreen( CalcColor( aSrcCol
.GetGreen(), nSrcAlpha
, nDstAlpha
, nResAlpha
, aDstCol
.GetGreen() ) );
1433 void OutputDevice::BlendBitmap(
1434 const SalTwoRect
& rPosAry
,
1435 const Bitmap
& rBmp
)
1437 mpGraphics
->BlendBitmap( rPosAry
, *rBmp
.ImplGetSalBitmap(), this );
1440 Bitmap
OutputDevice::BlendBitmapWithAlpha(
1442 BitmapReadAccess
const * pP
,
1443 BitmapReadAccess
const * pA
,
1444 const tools::Rectangle
& aDstRect
,
1445 const sal_Int32 nOffY
,
1446 const sal_Int32 nDstHeight
,
1447 const sal_Int32 nOffX
,
1448 const sal_Int32 nDstWidth
,
1453 BitmapColor aDstCol
;
1456 sal_uInt8 nResAlpha
;
1458 SAL_WARN_IF( !mpAlphaVDev
, "vcl.gdi", "BlendBitmapWithAlpha(): call me only with valid alpha VirtualDevice!" );
1460 bool bOldMapMode( mpAlphaVDev
->IsMapModeEnabled() );
1461 mpAlphaVDev
->EnableMapMode(false);
1463 Bitmap
aAlphaBitmap( mpAlphaVDev
->GetBitmap( aDstRect
.TopLeft(), aDstRect
.GetSize() ) );
1464 BitmapScopedWriteAccess
pAlphaW(aAlphaBitmap
);
1466 if( GetBitCount() <= 8 )
1468 Bitmap
aDither( aBmp
.GetSizePixel(), 8 );
1469 BitmapColor
aIndex( 0 );
1470 Bitmap::ScopedReadAccess
pB(aBmp
);
1471 BitmapScopedWriteAccess
pW(aDither
);
1473 if (pB
&& pP
&& pA
&& pW
&& pAlphaW
)
1477 for( nY
= 0, nOutY
= nOffY
; nY
< nDstHeight
; nY
++, nOutY
++ )
1479 const long nMapY
= pMapY
[ nY
];
1480 const long nModY
= ( nOutY
& 0x0FL
) << 4;
1483 Scanline pScanline
= pW
->GetScanline(nY
);
1484 Scanline pScanlineAlpha
= pAlphaW
->GetScanline(nY
);
1485 for( nX
= 0, nOutX
= nOffX
; nX
< nDstWidth
; nX
++, nOutX
++ )
1487 const long nMapX
= pMapX
[ nX
];
1488 const sal_uLong nD
= nVCLDitherLut
[ nModY
| ( nOutX
& 0x0FL
) ];
1490 aDstCol
= AlphaBlend( nX
, nY
, nMapX
, nMapY
, pP
, pA
, pB
.get(), pAlphaW
.get(), nResAlpha
);
1492 aIndex
.SetIndex( static_cast<sal_uInt8
>( nVCLRLut
[ ( nVCLLut
[ aDstCol
.GetRed() ] + nD
) >> 16 ] +
1493 nVCLGLut
[ ( nVCLLut
[ aDstCol
.GetGreen() ] + nD
) >> 16 ] +
1494 nVCLBLut
[ ( nVCLLut
[ aDstCol
.GetBlue() ] + nD
) >> 16 ] ) );
1495 pW
->SetPixelOnData( pScanline
, nX
, aIndex
);
1497 aIndex
.SetIndex( static_cast<sal_uInt8
>( nVCLRLut
[ ( nVCLLut
[ 255-nResAlpha
] + nD
) >> 16 ] +
1498 nVCLGLut
[ ( nVCLLut
[ 255-nResAlpha
] + nD
) >> 16 ] +
1499 nVCLBLut
[ ( nVCLLut
[ 255-nResAlpha
] + nD
) >> 16 ] ) );
1500 pAlphaW
->SetPixelOnData( pScanlineAlpha
, nX
, aIndex
);
1510 BitmapScopedWriteAccess
pB(aBmp
);
1511 if (pB
&& pP
&& pA
&& pAlphaW
)
1513 for( nY
= 0; nY
< nDstHeight
; nY
++ )
1515 const long nMapY
= pMapY
[ nY
];
1516 Scanline pScanlineB
= pB
->GetScanline(nY
);
1517 Scanline pScanlineAlpha
= pAlphaW
->GetScanline(nY
);
1519 for( nX
= 0; nX
< nDstWidth
; nX
++ )
1521 const long nMapX
= pMapX
[ nX
];
1522 aDstCol
= AlphaBlend( nX
, nY
, nMapX
, nMapY
, pP
, pA
, pB
.get(), pAlphaW
.get(), nResAlpha
);
1524 pB
->SetPixelOnData(pScanlineB
, nX
, pB
->GetBestMatchingColor(aDstCol
));
1525 pAlphaW
->SetPixelOnData(pScanlineAlpha
, nX
, pB
->GetBestMatchingColor(Color(255L-nResAlpha
, 255L-nResAlpha
, 255L-nResAlpha
)));
1534 mpAlphaVDev
->DrawBitmap( aDstRect
.TopLeft(), aAlphaBitmap
);
1535 mpAlphaVDev
->EnableMapMode( bOldMapMode
);
1540 Bitmap
OutputDevice::BlendBitmap(
1542 BitmapReadAccess
const * pP
,
1543 BitmapReadAccess
const * pA
,
1544 const sal_Int32 nOffY
,
1545 const sal_Int32 nDstHeight
,
1546 const sal_Int32 nOffX
,
1547 const sal_Int32 nDstWidth
,
1548 const tools::Rectangle
& aBmpRect
,
1555 BitmapColor aDstCol
;
1559 if( GetBitCount() <= 8 )
1561 Bitmap
aDither( aBmp
.GetSizePixel(), 8 );
1562 BitmapColor
aIndex( 0 );
1563 Bitmap::ScopedReadAccess
pB(aBmp
);
1564 BitmapScopedWriteAccess
pW(aDither
);
1566 if( pB
&& pP
&& pA
&& pW
)
1570 for( nY
= 0, nOutY
= nOffY
; nY
< nDstHeight
; nY
++, nOutY
++ )
1572 long nMapY
= pMapY
[ nY
];
1575 nMapY
= aBmpRect
.Bottom() - nMapY
;
1577 const long nModY
= ( nOutY
& 0x0FL
) << 4;
1580 Scanline pScanline
= pW
->GetScanline(nY
);
1581 Scanline pScanlineAlpha
= pA
->GetScanline(nMapY
);
1582 for( nX
= 0, nOutX
= nOffX
; nX
< nDstWidth
; nX
++, nOutX
++ )
1584 long nMapX
= pMapX
[ nX
];
1587 nMapX
= aBmpRect
.Right() - nMapX
;
1589 const sal_uLong nD
= nVCLDitherLut
[ nModY
| ( nOutX
& 0x0FL
) ];
1591 aDstCol
= pB
->GetColor( nY
, nX
);
1592 aDstCol
.Merge( pP
->GetColor( nMapY
, nMapX
), pA
->GetIndexFromData( pScanlineAlpha
, nMapX
) );
1593 aIndex
.SetIndex( static_cast<sal_uInt8
>( nVCLRLut
[ ( nVCLLut
[ aDstCol
.GetRed() ] + nD
) >> 16 ] +
1594 nVCLGLut
[ ( nVCLLut
[ aDstCol
.GetGreen() ] + nD
) >> 16 ] +
1595 nVCLBLut
[ ( nVCLLut
[ aDstCol
.GetBlue() ] + nD
) >> 16 ] ) );
1596 pW
->SetPixelOnData( pScanline
, nX
, aIndex
);
1607 BitmapScopedWriteAccess
pB(aBmp
);
1609 bool bFastBlend
= false;
1610 if( pP
&& pA
&& pB
&& !bHMirr
&& !bVMirr
)
1612 SalTwoRect
aTR(aBmpRect
.Left(), aBmpRect
.Top(), aBmpRect
.GetWidth(), aBmpRect
.GetHeight(),
1613 nOffX
, nOffY
, aOutSz
.Width(), aOutSz
.Height());
1615 bFastBlend
= ImplFastBitmapBlending( *pB
,*pP
,*pA
, aTR
);
1618 if( pP
&& pA
&& pB
&& !bFastBlend
)
1620 switch( pP
->GetScanlineFormat() )
1622 case ScanlineFormat::N8BitPal
:
1624 for( nY
= 0; nY
< nDstHeight
; nY
++ )
1626 long nMapY
= pMapY
[ nY
];
1629 nMapY
= aBmpRect
.Bottom() - nMapY
;
1631 Scanline pPScan
= pP
->GetScanline( nMapY
);
1632 Scanline pAScan
= pA
->GetScanline( nMapY
);
1633 Scanline pBScan
= pB
->GetScanline( nY
);
1635 for( nX
= 0; nX
< nDstWidth
; nX
++ )
1637 long nMapX
= pMapX
[ nX
];
1641 nMapX
= aBmpRect
.Right() - nMapX
;
1643 aDstCol
= pB
->GetPixelFromData( pBScan
, nX
);
1644 aDstCol
.Merge( pP
->GetPaletteColor( pPScan
[ nMapX
] ), pAScan
[ nMapX
] );
1645 pB
->SetPixelOnData( pBScan
, nX
, aDstCol
);
1654 for( nY
= 0; nY
< nDstHeight
; nY
++ )
1656 long nMapY
= pMapY
[ nY
];
1660 nMapY
= aBmpRect
.Bottom() - nMapY
;
1662 Scanline pAScan
= pA
->GetScanline( nMapY
);
1663 Scanline pBScan
= pB
->GetScanline(nY
);
1664 for( nX
= 0; nX
< nDstWidth
; nX
++ )
1666 long nMapX
= pMapX
[ nX
];
1670 nMapX
= aBmpRect
.Right() - nMapX
;
1672 aDstCol
= pB
->GetPixelFromData( pBScan
, nX
);
1673 aDstCol
.Merge( pP
->GetColor( nMapY
, nMapX
), pAScan
[ nMapX
] );
1674 pB
->SetPixelOnData( pBScan
, nX
, aDstCol
);
1689 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */