1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
20 #include <sal/config.h>
28 #include "gdiimpl.hxx"
31 #include <rtl/strbuf.hxx>
32 #include <sal/log.hxx>
33 #include <tools/poly.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 #include <win/wincomp.hxx>
38 #include <win/saldata.hxx>
39 #include <win/salgdi.h>
40 #include <win/salbmp.h>
41 #include <win/scoped_gdi.hxx>
42 #include <vcl/BitmapAccessMode.hxx>
43 #include <vcl/BitmapBuffer.hxx>
44 #include <vcl/BitmapPalette.hxx>
45 #include <win/salframe.h>
46 #include <basegfx/matrix/b2dhommatrixtools.hxx>
47 #include <basegfx/utils/systemdependentdata.hxx>
49 #include <win/salids.hrc>
50 #include <ControlCacheKey.hxx>
55 #include <gdiplusenums.h>
56 #include <gdipluscolor.h>
60 #define SAL_POLYPOLYCOUNT_STACKBUF 8
61 #define SAL_POLYPOLYPOINTS_STACKBUF 64
63 #define SAL_POLY_STACKBUF 32
67 // #100127# Fill point and flag memory from array of points which
68 // might also contain bezier control points for the PolyDraw() GDI method
69 // Make sure pWinPointAry and pWinFlagAry are big enough
70 void ImplPreparePolyDraw( bool bCloseFigures
,
72 const sal_uInt32
* pPoints
,
73 const Point
* const* pPtAry
,
74 const PolyFlags
* const* pFlgAry
,
79 for( nCurrPoly
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
81 const Point
* pCurrPoint
= *pPtAry
++;
82 const PolyFlags
* pCurrFlag
= *pFlgAry
++;
83 const sal_uInt32 nCurrPoints
= *pPoints
++;
84 const bool bHaveFlagArray( pCurrFlag
);
90 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
92 *pWinFlagAry
++ = PT_MOVETO
;
95 for( nCurrPoint
=1; nCurrPoint
<nCurrPoints
; )
97 // #102067# Check existence of flag array
99 ( nCurrPoint
+ 2 ) < nCurrPoints
)
101 PolyFlags
P4( pCurrFlag
[ 2 ] );
103 if( ( PolyFlags::Control
== pCurrFlag
[ 0 ] ) &&
104 ( PolyFlags::Control
== pCurrFlag
[ 1 ] ) &&
105 ( PolyFlags::Normal
== P4
|| PolyFlags::Smooth
== P4
|| PolyFlags::Symmetric
== P4
) )
108 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
110 *pWinFlagAry
++ = PT_BEZIERTO
;
113 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
115 *pWinFlagAry
++ = PT_BEZIERTO
;
118 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
120 *pWinFlagAry
++ = PT_BEZIERTO
;
128 // regular line point
129 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
131 *pWinFlagAry
++ = PT_LINETO
;
138 pWinFlagAry
[-1] |= PT_CLOSEFIGURE
;
143 Color
ImplGetROPColor( SalROPColor nROPColor
)
146 if ( nROPColor
== SalROPColor::N0
)
147 nColor
= Color( 0, 0, 0 );
149 nColor
= Color( 255, 255, 255 );
153 bool IsDitherColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
155 constexpr sal_uInt8 DITHER_PAL_DELTA
= 51;
157 return !(nRed
% DITHER_PAL_DELTA
) &&
158 !(nGreen
% DITHER_PAL_DELTA
) &&
159 !(nBlue
% DITHER_PAL_DELTA
);
162 bool IsPaletteColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
164 static const PALETTEENTRY aImplSalSysPalEntryAry
[] =
169 { 0, 0x80, 0x80, 0 },
171 { 0x80, 0, 0x80, 0 },
172 { 0x80, 0x80, 0, 0 },
173 { 0x80, 0x80, 0x80, 0 },
174 { 0xC0, 0xC0, 0xC0, 0 },
177 { 0, 0xFF, 0xFF, 0 },
179 { 0xFF, 0, 0xFF, 0 },
180 { 0xFF, 0xFF, 0, 0 },
181 { 0xFF, 0xFF, 0xFF, 0 }
184 for (const auto& rPalEntry
: aImplSalSysPalEntryAry
)
186 if(rPalEntry
.peRed
== nRed
&&
187 rPalEntry
.peGreen
== nGreen
&&
188 rPalEntry
.peBlue
== nBlue
)
197 bool IsExtraColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
199 return (nRed
== 0) && (nGreen
== 184) && (nBlue
== 255);
202 bool ImplIsPaletteEntry(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
204 return IsDitherColor(nRed
, nGreen
, nBlue
) ||
205 IsPaletteColor(nRed
, nGreen
, nBlue
) ||
206 IsExtraColor(nRed
, nGreen
, nBlue
);
211 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics
& rParent
):
223 WinSalGraphicsImpl::~WinSalGraphicsImpl()
234 DeleteBrush( mhBrush
);
238 void WinSalGraphicsImpl::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
244 hSrcDC
= static_cast<WinSalGraphics
*>(pSrcGraphics
)->getHDC();
246 hSrcDC
= mrParent
.getHDC();
253 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
254 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
256 BitBlt( mrParent
.getHDC(),
257 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
258 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
260 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
265 int nOldStretchMode
= SetStretchBltMode( mrParent
.getHDC(), STRETCH_DELETESCANS
);
266 StretchBlt( mrParent
.getHDC(),
267 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
268 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
270 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
271 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
273 SetStretchBltMode( mrParent
.getHDC(), nOldStretchMode
);
280 void MakeInvisibleArea(const RECT
& rSrcRect
,
281 int nLeft
, int nTop
, int nRight
, int nBottom
,
282 HRGN
& rhInvalidateRgn
)
284 if (!rhInvalidateRgn
)
286 rhInvalidateRgn
= CreateRectRgnIndirect(&rSrcRect
);
289 ScopedHRGN
hTempRgn(CreateRectRgn(nLeft
, nTop
, nRight
, nBottom
));
290 CombineRgn(rhInvalidateRgn
, rhInvalidateRgn
, hTempRgn
.get(), RGN_DIFF
);
293 void ImplCalcOutSideRgn( const RECT
& rSrcRect
,
294 int nLeft
, int nTop
, int nRight
, int nBottom
,
295 HRGN
& rhInvalidateRgn
)
297 // calculate area outside the visible region
298 if (rSrcRect
.left
< nLeft
)
300 MakeInvisibleArea(rSrcRect
, -31999, 0, nLeft
, 31999, rhInvalidateRgn
);
302 if (rSrcRect
.top
< nTop
)
304 MakeInvisibleArea(rSrcRect
, 0, -31999, 31999, nTop
, rhInvalidateRgn
);
306 if (rSrcRect
.right
> nRight
)
308 MakeInvisibleArea(rSrcRect
, nRight
, 0, 31999, 31999, rhInvalidateRgn
);
310 if (rSrcRect
.bottom
> nBottom
)
312 MakeInvisibleArea(rSrcRect
, 0, nBottom
, 31999, 31999, rhInvalidateRgn
);
318 void WinSalGraphicsImpl::copyArea( tools::Long nDestX
, tools::Long nDestY
,
319 tools::Long nSrcX
, tools::Long nSrcY
,
320 tools::Long nSrcWidth
, tools::Long nSrcHeight
,
321 bool bWindowInvalidate
)
323 bool bRestoreClipRgn
= false;
324 HRGN hOldClipRgn
= nullptr;
325 int nOldClipRgnType
= ERROR
;
326 HRGN hInvalidateRgn
= nullptr;
328 // do we have to invalidate also the overlapping regions?
329 if ( bWindowInvalidate
&& mrParent
.isWindow() )
331 // compute and invalidate those parts that were either off-screen or covered by other windows
332 // while performing the above BitBlt
333 // those regions then have to be invalidated as they contain useless/wrong data
341 // restrict srcRect to this window (calc intersection)
342 aSrcRect
.left
= static_cast<int>(nSrcX
);
343 aSrcRect
.top
= static_cast<int>(nSrcY
);
344 aSrcRect
.right
= aSrcRect
.left
+static_cast<int>(nSrcWidth
);
345 aSrcRect
.bottom
= aSrcRect
.top
+static_cast<int>(nSrcHeight
);
346 GetClientRect( mrParent
.gethWnd(), &aClipRect
);
347 if ( IntersectRect( &aSrcRect
, &aSrcRect
, &aClipRect
) )
349 // transform srcRect to screen coordinates
353 ClientToScreen( mrParent
.gethWnd(), &aPt
);
354 aSrcRect
.left
+= aPt
.x
;
355 aSrcRect
.top
+= aPt
.y
;
356 aSrcRect
.right
+= aPt
.x
;
357 aSrcRect
.bottom
+= aPt
.y
;
358 hInvalidateRgn
= nullptr;
360 // compute the parts that are off screen (ie invisible)
362 ImplSalGetWorkArea( nullptr, &theScreen
, nullptr ); // find the screen area taking multiple monitors into account
363 ImplCalcOutSideRgn( aSrcRect
, theScreen
.left
, theScreen
.top
, theScreen
.right
, theScreen
.bottom
, hInvalidateRgn
);
365 // calculate regions that are covered by other windows
366 HRGN hTempRgn2
= nullptr;
367 HWND hWndTopWindow
= mrParent
.gethWnd();
368 // Find the TopLevel Window, because only Windows which are in
369 // in the foreground of our TopLevel window must be considered
370 if ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
)
372 RECT aTempRect3
= aSrcRect
;
375 hWndTopWindow
= ::GetParent( hWndTopWindow
);
377 // Test if the Parent clips our window
378 GetClientRect( hWndTopWindow
, &aTempRect
);
382 ClientToScreen( hWndTopWindow
, &aPt2
);
383 aTempRect
.left
+= aPt2
.x
;
384 aTempRect
.top
+= aPt2
.y
;
385 aTempRect
.right
+= aPt2
.x
;
386 aTempRect
.bottom
+= aPt2
.y
;
387 IntersectRect( &aTempRect3
, &aTempRect3
, &aTempRect
);
389 while ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
);
391 // If one or more Parents clip our window, then we must
392 // calculate the outside area
393 if ( !EqualRect( &aSrcRect
, &aTempRect3
) )
395 ImplCalcOutSideRgn( aSrcRect
,
396 aTempRect3
.left
, aTempRect3
.top
,
397 aTempRect3
.right
, aTempRect3
.bottom
,
401 // retrieve the top-most (z-order) child window
402 hWnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
405 if ( hWnd
== hWndTopWindow
)
407 if ( IsWindowVisible( hWnd
) && !IsIconic( hWnd
) )
409 GetWindowRect( hWnd
, &aTempRect
);
410 if ( IntersectRect( &aTempRect2
, &aSrcRect
, &aTempRect
) )
412 // hWnd covers part or all of aSrcRect
413 if ( !hInvalidateRgn
)
414 hInvalidateRgn
= CreateRectRgnIndirect( &aSrcRect
);
416 // get full bounding box of hWnd
417 hTempRgn
= CreateRectRgnIndirect( &aTempRect
);
419 // get region of hWnd (the window may be shaped)
421 hTempRgn2
= CreateRectRgn( 0, 0, 0, 0 );
422 int nRgnType
= GetWindowRgn( hWnd
, hTempRgn2
);
423 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
425 // convert window region to screen coordinates
426 OffsetRgn( hTempRgn2
, aTempRect
.left
, aTempRect
.top
);
427 // and intersect with the window's bounding box
428 CombineRgn( hTempRgn
, hTempRgn
, hTempRgn2
, RGN_AND
);
430 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
431 CombineRgn( hInvalidateRgn
, hInvalidateRgn
, hTempRgn
, RGN_DIFF
);
432 DeleteRegion( hTempRgn
);
435 // retrieve the next window in the z-order, i.e. the window below hwnd
436 hWnd
= GetWindow( hWnd
, GW_HWNDNEXT
);
439 DeleteRegion( hTempRgn2
);
440 if ( hInvalidateRgn
)
442 // hInvalidateRgn contains the fully visible parts of the original srcRect
443 hTempRgn
= CreateRectRgnIndirect( &aSrcRect
);
444 // subtract it from the original rect to get the occluded parts
445 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_DIFF
);
446 DeleteRegion( hTempRgn
);
448 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
450 // move the occluded parts to the destination pos
451 int nOffX
= static_cast<int>(nDestX
-nSrcX
);
452 int nOffY
= static_cast<int>(nDestY
-nSrcY
);
453 OffsetRgn( hInvalidateRgn
, nOffX
-aPt
.x
, nOffY
-aPt
.y
);
455 // by excluding hInvalidateRgn from the system's clip region
456 // we will prevent bitblt from copying useless data
457 // especially now shadows from overlapping windows will appear (#i36344)
458 hOldClipRgn
= CreateRectRgn( 0, 0, 0, 0 );
459 nOldClipRgnType
= GetClipRgn( mrParent
.getHDC(), hOldClipRgn
);
461 bRestoreClipRgn
= true; // indicate changed clipregion and force invalidate
462 ExtSelectClipRgn( mrParent
.getHDC(), hInvalidateRgn
, RGN_DIFF
);
468 BitBlt( mrParent
.getHDC(),
469 static_cast<int>(nDestX
), static_cast<int>(nDestY
),
470 static_cast<int>(nSrcWidth
), static_cast<int>(nSrcHeight
),
472 static_cast<int>(nSrcX
), static_cast<int>(nSrcY
),
475 if( bRestoreClipRgn
)
477 // restore old clip region
478 if( nOldClipRgnType
!= ERROR
)
479 SelectClipRgn( mrParent
.getHDC(), hOldClipRgn
);
480 DeleteRegion( hOldClipRgn
);
482 // invalidate regions that were not copied
483 bool bInvalidate
= true;
485 // Combine Invalidate vcl::Region with existing ClipRegion
486 HRGN hTempRgn
= CreateRectRgn( 0, 0, 0, 0 );
487 if ( GetClipRgn( mrParent
.getHDC(), hTempRgn
) == 1 )
489 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_AND
);
490 if ( (nRgnType
== ERROR
) || (nRgnType
== NULLREGION
) )
493 DeleteRegion( hTempRgn
);
497 InvalidateRgn( mrParent
.gethWnd(), hInvalidateRgn
, TRUE
);
498 // here we only initiate an update if this is the MainThread,
499 // so that there is no deadlock when handling the Paint event,
500 // as the SolarMutex is already held by this Thread
501 SalData
* pSalData
= GetSalData();
502 DWORD nCurThreadId
= GetCurrentThreadId();
503 if ( pSalData
->mnAppThreadId
== nCurThreadId
)
504 UpdateWindow( mrParent
.gethWnd() );
507 DeleteRegion( hInvalidateRgn
);
514 void ImplDrawBitmap( HDC hDC
, const SalTwoRect
& rPosAry
, const WinSalBitmap
& rSalBitmap
,
515 bool bPrinter
, int nDrawMode
)
520 HBITMAP hDrawDDB
= rSalBitmap
.ImplGethDDB();
521 std::optional
<WinSalBitmap
> xTmpSalBmp
;
522 bool bPrintDDB
= ( bPrinter
&& hDrawDDB
);
526 xTmpSalBmp
.emplace();
527 xTmpSalBmp
->Create(rSalBitmap
, vcl::bitDepthToPixelFormat(rSalBitmap
.GetBitCount()));
528 hDrawDIB
= xTmpSalBmp
->ImplGethDIB();
531 hDrawDIB
= rSalBitmap
.ImplGethDIB();
535 if (PBITMAPINFO pBI
= static_cast<PBITMAPINFO
>(GlobalLock( hDrawDIB
)))
537 PBYTE pBits
= reinterpret_cast<PBYTE
>(pBI
) + pBI
->bmiHeader
.biSize
+
538 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB
) * sizeof( RGBQUAD
);
539 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
542 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
543 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
544 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(pBI
->bmiHeader
.biHeight
- rPosAry
.mnSrcHeight
- rPosAry
.mnSrcY
),
545 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
546 pBits
, pBI
, DIB_RGB_COLORS
, nDrawMode
);
548 GlobalUnlock( hDrawDIB
);
549 SetStretchBltMode( hDC
, nOldStretchMode
);
552 else if( hDrawDDB
&& !bPrintDDB
)
554 ScopedCachedHDC
<CACHED_HDC_DRAW
> hBmpDC(hDrawDDB
);
556 COLORREF nOldBkColor
= RGB(0xFF,0xFF,0xFF);
557 COLORREF nOldTextColor
= RGB(0,0,0);
558 bool bMono
= ( rSalBitmap
.GetBitCount() == 1 );
562 COLORREF nBkColor
= RGB( 0xFF, 0xFF, 0xFF );
563 COLORREF nTextColor
= RGB( 0x00, 0x00, 0x00 );
564 //fdo#33455 handle 1 bit depth pngs with palette entries
565 //to set fore/back colors
566 if (BitmapBuffer
* pBitmapBuffer
= const_cast<WinSalBitmap
&>(rSalBitmap
).AcquireBuffer(BitmapAccessMode::Info
))
568 const BitmapPalette
& rPalette
= pBitmapBuffer
->maPalette
;
569 if (rPalette
.GetEntryCount() == 2)
571 Color nCol
= rPalette
[0];
572 nTextColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
574 nBkColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
576 const_cast<WinSalBitmap
&>(rSalBitmap
).ReleaseBuffer(pBitmapBuffer
, BitmapAccessMode::Info
);
578 nOldBkColor
= SetBkColor( hDC
, nBkColor
);
579 nOldTextColor
= ::SetTextColor( hDC
, nTextColor
);
582 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
583 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
586 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
587 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
589 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
594 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
597 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
598 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
600 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
601 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
604 SetStretchBltMode( hDC
, nOldStretchMode
);
609 SetBkColor( hDC
, nOldBkColor
);
610 ::SetTextColor( hDC
, nOldTextColor
);
618 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
620 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
624 // only paint direct when no scaling and no MapMode, else the
625 // more expensive conversions may be done for short-time Bitmap/BitmapEx
626 // used for buffering only
627 if(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&& rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
)
629 bTryDirectPaint
= false;
633 // try to draw using GdiPlus directly
634 if(bTryDirectPaint
&& TryDrawBitmapGDIPlus(rPosAry
, rSalBitmap
))
639 // fall back old stuff
640 assert(dynamic_cast<const WinSalBitmap
*>(&rSalBitmap
));
642 ImplDrawBitmap(mrParent
.getHDC(), rPosAry
, static_cast<const WinSalBitmap
&>(rSalBitmap
),
643 mrParent
.isPrinter(),
644 mbXORMode
? SRCINVERT
: SRCCOPY
);
647 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect
& rPosAry
,
648 const SalBitmap
& rSSalBitmap
,
649 const SalBitmap
& rSTransparentBitmap
)
651 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
652 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
654 // try to draw using GdiPlus directly
655 if(bTryDirectPaint
&& drawAlphaBitmap(rPosAry
, rSSalBitmap
, rSTransparentBitmap
))
660 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
661 assert(dynamic_cast<const WinSalBitmap
*>(&rSTransparentBitmap
));
663 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
664 const WinSalBitmap
& rTransparentBitmap
= static_cast<const WinSalBitmap
&>(rSTransparentBitmap
);
666 SalTwoRect aPosAry
= rPosAry
;
667 int nDstX
= static_cast<int>(aPosAry
.mnDestX
);
668 int nDstY
= static_cast<int>(aPosAry
.mnDestY
);
669 int nDstWidth
= static_cast<int>(aPosAry
.mnDestWidth
);
670 int nDstHeight
= static_cast<int>(aPosAry
.mnDestHeight
);
671 HDC hDC
= mrParent
.getHDC();
673 ScopedHBITMAP hMemBitmap
;
674 ScopedHBITMAP hMaskBitmap
;
676 if( ( nDstWidth
> CACHED_HDC_DEFEXT
) || ( nDstHeight
> CACHED_HDC_DEFEXT
) )
678 hMemBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
679 hMaskBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
682 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(hMemBitmap
.get());
683 ScopedCachedHDC
<CACHED_HDC_2
> hMaskDC(hMaskBitmap
.get());
685 aPosAry
.mnDestX
= aPosAry
.mnDestY
= 0;
686 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hDC
, nDstX
, nDstY
, SRCCOPY
);
688 // WIN/WNT seems to have a minor problem mapping the correct color of the
689 // mask to the palette if we draw the DIB directly ==> draw DDB
690 if( ( GetBitCount() <= 8 ) && rTransparentBitmap
.ImplGethDIB() && rTransparentBitmap
.GetBitCount() == 1 )
694 if( aTmp
.Create( rTransparentBitmap
, &mrParent
) )
695 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, aTmp
, false, SRCCOPY
);
698 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rTransparentBitmap
, false, SRCCOPY
);
700 // now MemDC contains background, MaskDC the transparency mask
702 // #105055# Respect XOR mode
705 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
706 // now MaskDC contains the bitmap area with black background
707 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCINVERT
);
708 // now MemDC contains background XORed bitmap area on top
712 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCAND
);
713 // now MemDC contains background with masked-out bitmap area
714 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
715 // now MaskDC contains the bitmap area with black background
716 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCPAINT
);
717 // now MemDC contains background and bitmap merged together
720 BitBlt( hDC
, nDstX
, nDstY
, nDstWidth
, nDstHeight
, hMemDC
.get(), 0, 0, SRCCOPY
);
723 bool WinSalGraphicsImpl::drawAlphaRect( tools::Long nX
, tools::Long nY
, tools::Long nWidth
,
724 tools::Long nHeight
, sal_uInt8 nTransparency
)
726 if( mbPen
|| !mbBrush
|| mbXORMode
)
727 return false; // can only perform solid fills without XOR.
729 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(nullptr);
730 SetPixel( hMemDC
.get(), int(0), int(0), mnBrushColor
);
732 BLENDFUNCTION aFunc
= {
735 sal::static_int_cast
<sal_uInt8
>(255 - 255L*nTransparency
/100),
739 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
741 bool bRet
= GdiAlphaBlend(mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
,
742 hMemDC
.get(), 0,0,1,1,
748 void WinSalGraphicsImpl::drawMask(const SalTwoRect
& rPosAry
,
749 const SalBitmap
& rSSalBitmap
,
752 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
754 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
756 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
758 SalTwoRect aPosAry
= rPosAry
;
759 const HDC hDC
= mrParent
.getHDC();
761 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(RGB(nMaskColor
.GetRed(),
762 nMaskColor
.GetGreen(),
763 nMaskColor
.GetBlue())));
765 // WIN/WNT seems to have a minor problem mapping the correct color of the
766 // mask to the palette if we draw the DIB directly ==> draw DDB
767 if( ( GetBitCount() <= 8 ) && rSalBitmap
.ImplGethDIB() && rSalBitmap
.GetBitCount() == 1 )
771 if( aTmp
.Create( rSalBitmap
, &mrParent
) )
772 ImplDrawBitmap( hDC
, aPosAry
, aTmp
, false, 0x00B8074AUL
);
775 ImplDrawBitmap( hDC
, aPosAry
, rSalBitmap
, false, 0x00B8074AUL
);
778 std::shared_ptr
<SalBitmap
> WinSalGraphicsImpl::getBitmap( tools::Long nX
, tools::Long nY
, tools::Long nDX
, tools::Long nDY
)
780 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
782 std::shared_ptr
<WinSalBitmap
> pSalBitmap
;
784 nDX
= std::abs( nDX
);
785 nDY
= std::abs( nDY
);
787 HDC hDC
= mrParent
.getHDC();
788 HBITMAP hBmpBitmap
= CreateCompatibleBitmap( hDC
, nDX
, nDY
);
792 ScopedCachedHDC
<CACHED_HDC_1
> hBmpDC(hBmpBitmap
);
794 bRet
= BitBlt(hBmpDC
.get(), 0, 0,
795 static_cast<int>(nDX
), static_cast<int>(nDY
), hDC
,
796 static_cast<int>(nX
), static_cast<int>(nY
), SRCCOPY
) ? TRUE
: FALSE
;
801 pSalBitmap
= std::make_shared
<WinSalBitmap
>();
803 if( !pSalBitmap
->Create( hBmpBitmap
) )
810 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
811 DeleteBitmap( hBmpBitmap
);
817 Color
WinSalGraphicsImpl::getPixel( tools::Long nX
, tools::Long nY
)
819 COLORREF aWinCol
= ::GetPixel( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
) );
821 if ( CLR_INVALID
== aWinCol
)
822 return Color( 0, 0, 0 );
824 return Color( GetRValue( aWinCol
),
825 GetGValue( aWinCol
),
826 GetBValue( aWinCol
) );
832 HBRUSH
Get50PercentBrush()
834 SalData
* pSalData
= GetSalData();
835 if ( !pSalData
->mh50Brush
)
837 if ( !pSalData
->mh50Bmp
)
838 pSalData
->mh50Bmp
= ImplLoadSalBitmap( SAL_RESID_BITMAP_50
);
839 pSalData
->mh50Brush
= CreatePatternBrush( pSalData
->mh50Bmp
);
842 return pSalData
->mh50Brush
;
847 void WinSalGraphicsImpl::invert( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
, SalInvert nFlags
)
849 if ( nFlags
& SalInvert::TrackFrame
)
851 HPEN hDotPen
= CreatePen( PS_DOT
, 0, 0 );
852 HPEN hOldPen
= SelectPen( mrParent
.getHDC(), hDotPen
);
853 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), GetStockBrush( NULL_BRUSH
) );
854 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
856 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
858 SetROP2( mrParent
.getHDC(), nOldROP
);
859 SelectPen( mrParent
.getHDC(), hOldPen
);
860 SelectBrush( mrParent
.getHDC(), hOldBrush
);
861 DeletePen( hDotPen
);
863 else if ( nFlags
& SalInvert::N50
)
865 COLORREF nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
866 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), Get50PercentBrush() );
867 PatBlt( mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
, PATINVERT
);
868 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
869 SelectBrush( mrParent
.getHDC(), hOldBrush
);
874 aRect
.left
= static_cast<int>(nX
);
875 aRect
.top
= static_cast<int>(nY
);
876 aRect
.right
= static_cast<int>(nX
)+nWidth
;
877 aRect
.bottom
= static_cast<int>(nY
)+nHeight
;
878 ::InvertRect( mrParent
.getHDC(), &aRect
);
882 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints
, const Point
* pPtAry
, SalInvert nSalFlags
)
887 HBRUSH hOldBrush
= nullptr;
888 COLORREF nOldTextColor
RGB(0,0,0);
889 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
891 if ( nSalFlags
& SalInvert::TrackFrame
)
892 hPen
= CreatePen( PS_DOT
, 0, 0 );
896 if ( nSalFlags
& SalInvert::N50
)
897 hBrush
= Get50PercentBrush();
899 hBrush
= GetStockBrush( BLACK_BRUSH
);
901 hPen
= GetStockPen( NULL_PEN
);
902 nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
903 hOldBrush
= SelectBrush( mrParent
.getHDC(), hBrush
);
905 hOldPen
= SelectPen( mrParent
.getHDC(), hPen
);
907 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
908 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
909 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
911 // for Windows 95 and its maximum number of points
912 if ( nSalFlags
& SalInvert::TrackFrame
)
914 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
915 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
919 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
920 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
923 SetROP2( mrParent
.getHDC(), nOldROP
);
924 SelectPen( mrParent
.getHDC(), hOldPen
);
926 if ( nSalFlags
& SalInvert::TrackFrame
)
930 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
931 SelectBrush( mrParent
.getHDC(), hOldBrush
);
935 sal_uInt16
WinSalGraphicsImpl::GetBitCount() const
937 return static_cast<sal_uInt16
>(GetDeviceCaps( mrParent
.getHDC(), BITSPIXEL
));
940 tools::Long
WinSalGraphicsImpl::GetGraphicsWidth() const
942 if( mrParent
.gethWnd() && IsWindow( mrParent
.gethWnd() ) )
944 WinSalFrame
* pFrame
= GetWindowPtr( mrParent
.gethWnd() );
947 if (pFrame
->GetWidth())
948 return pFrame
->GetWidth();
951 // TODO: perhaps not needed, width should always be up-to-date
953 GetClientRect( mrParent
.gethWnd(), &aRect
);
962 void WinSalGraphicsImpl::ResetClipRegion()
964 if ( mrParent
.mhRegion
)
966 DeleteRegion( mrParent
.mhRegion
);
967 mrParent
.mhRegion
= nullptr;
970 SelectClipRgn( mrParent
.getHDC(), nullptr );
973 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon
& rCandidate
)
975 if(rCandidate
.areControlPointsUsed())
980 const sal_uInt32
nPointCount(rCandidate
.count());
987 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
+ 1 : nPointCount
);
988 basegfx::B2DPoint
aLast(rCandidate
.getB2DPoint(0));
990 for(sal_uInt32
a(1); a
< nEdgeCount
; a
++)
992 const sal_uInt32
nNextIndex(a
% nPointCount
);
993 const basegfx::B2DPoint
aCurrent(rCandidate
.getB2DPoint(nNextIndex
));
995 if(!basegfx::fTools::equal(aLast
.getX(), aCurrent
.getX()) && !basegfx::fTools::equal(aLast
.getY(), aCurrent
.getY()))
1006 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon
& rCandidate
)
1008 if(rCandidate
.areControlPointsUsed())
1013 for(auto const& rPolygon
: rCandidate
)
1015 if(!containsOnlyHorizontalAndVerticalEdges(rPolygon
))
1024 void WinSalGraphicsImpl::setClipRegion( const vcl::Region
& i_rClip
)
1026 if ( mrParent
.mhRegion
)
1028 DeleteRegion( mrParent
.mhRegion
);
1029 mrParent
.mhRegion
= nullptr;
1032 bool bUsePolygon(i_rClip
.HasPolyPolygonOrB2DPolyPolygon());
1033 static bool bTryToAvoidPolygon(true);
1035 // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1036 // and only contains horizontal/vertical edges. In that case, use the fallback
1037 // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1038 // the correct polygon-to-RegionBand transformation.
1039 // Background is that when using the same Rectangle as rectangle or as Polygon
1040 // clip region will lead to different results; the polygon-based one will be
1041 // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1042 // again is because of the polygon-nature and it's classic handling when filling.
1043 // This also means that all cases which use a 'true' polygon-based incarnation of
1044 // a vcl::Region should know what they do - it may lead to repaint errors.
1045 if(bUsePolygon
&& bTryToAvoidPolygon
)
1047 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1049 if(!aPolyPolygon
.areControlPointsUsed())
1051 if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon
))
1053 bUsePolygon
= false;
1060 // #i122149# check the comment above to know that this may lead to potential repaint
1061 // problems. It may be solved (if needed) by scaling the polygon by one in X
1062 // and Y. Currently the workaround to only use it if really unavoidable will
1063 // solve most cases. When someone is really using polygon-based Regions he
1064 // should know what he is doing.
1065 // Added code to do that scaling to check if it works, testing it.
1066 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1067 const sal_uInt32
nCount(aPolyPolygon
.count());
1071 std::vector
< POINT
> aPolyPoints
;
1072 aPolyPoints
.reserve( 1024 );
1073 std::vector
< INT
> aPolyCounts( nCount
, 0 );
1074 basegfx::B2DHomMatrix aExpand
;
1075 sal_uInt32
nTargetCount(0);
1076 static bool bExpandByOneInXandY(true);
1078 if(bExpandByOneInXandY
)
1080 const basegfx::B2DRange
aRangeS(aPolyPolygon
.getB2DRange());
1081 const basegfx::B2DRange
aRangeT(aRangeS
.getMinimum(), aRangeS
.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1082 aExpand
= basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS
, aRangeT
);
1085 for(auto const& rPolygon
: aPolyPolygon
)
1087 const basegfx::B2DPolygon
aPoly(
1088 basegfx::utils::adaptiveSubdivideByDistance(
1091 const sal_uInt32
nPoints(aPoly
.count());
1093 // tdf#40863 For CustomShapes there is a hack (see
1094 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1095 // with a single point in top-left and bottom-right corner
1096 // of the BoundRect to be able to determine the correct BoundRect
1097 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1098 // fails with polygons containing a single pixel, so clipping is
1099 // lost. For now, use only polygons with more than two points - the
1100 // ones that may have an area.
1101 // Note: polygons with one point which are curves may have an area,
1102 // but the polygon is already subdivided here, so no need to test
1106 aPolyCounts
[nTargetCount
] = nPoints
;
1109 for( sal_uInt32 b
= 0; b
< nPoints
; b
++ )
1111 basegfx::B2DPoint
aPt(aPoly
.getB2DPoint(b
));
1113 if(bExpandByOneInXandY
)
1115 aPt
= aExpand
* aPt
;
1119 // #i122149# do correct rounding
1120 aPOINT
.x
= basegfx::fround(aPt
.getX());
1121 aPOINT
.y
= basegfx::fround(aPt
.getY());
1122 aPolyPoints
.push_back( aPOINT
);
1129 mrParent
.mhRegion
= CreatePolyPolygonRgn( aPolyPoints
.data(), aPolyCounts
.data(), nTargetCount
, ALTERNATE
);
1135 RectangleVector aRectangles
;
1136 i_rClip
.GetRegionRectangles(aRectangles
);
1138 sal_uLong nRectBufSize
= sizeof(RECT
)*aRectangles
.size();
1139 if ( aRectangles
.size() < SAL_CLIPRECT_COUNT
)
1141 if ( !mrParent
.mpStdClipRgnData
)
1142 mrParent
.mpStdClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+(SAL_CLIPRECT_COUNT
*sizeof(RECT
))]);
1143 mrParent
.mpClipRgnData
= mrParent
.mpStdClipRgnData
;
1146 mrParent
.mpClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+nRectBufSize
]);
1147 mrParent
.mpClipRgnData
->rdh
.dwSize
= sizeof( RGNDATAHEADER
);
1148 mrParent
.mpClipRgnData
->rdh
.iType
= RDH_RECTANGLES
;
1149 mrParent
.mpClipRgnData
->rdh
.nCount
= aRectangles
.size();
1150 mrParent
.mpClipRgnData
->rdh
.nRgnSize
= nRectBufSize
;
1151 RECT
* pBoundRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1152 SetRectEmpty( pBoundRect
);
1153 RECT
* pNextClipRect
= reinterpret_cast<RECT
*>(&(mrParent
.mpClipRgnData
->Buffer
));
1154 bool bFirstClipRect
= true;
1156 for (auto const& rectangle
: aRectangles
)
1158 const tools::Long
nW(rectangle
.GetWidth());
1159 const tools::Long
nH(rectangle
.GetHeight());
1163 const tools::Long
nRight(rectangle
.Left() + nW
);
1164 const tools::Long
nBottom(rectangle
.Top() + nH
);
1168 pBoundRect
->left
= rectangle
.Left();
1169 pBoundRect
->top
= rectangle
.Top();
1170 pBoundRect
->right
= nRight
;
1171 pBoundRect
->bottom
= nBottom
;
1172 bFirstClipRect
= false;
1176 if(rectangle
.Left() < pBoundRect
->left
)
1178 pBoundRect
->left
= static_cast<int>(rectangle
.Left());
1181 if(rectangle
.Top() < pBoundRect
->top
)
1183 pBoundRect
->top
= static_cast<int>(rectangle
.Top());
1186 if(nRight
> pBoundRect
->right
)
1188 pBoundRect
->right
= static_cast<int>(nRight
);
1191 if(nBottom
> pBoundRect
->bottom
)
1193 pBoundRect
->bottom
= static_cast<int>(nBottom
);
1197 pNextClipRect
->left
= static_cast<int>(rectangle
.Left());
1198 pNextClipRect
->top
= static_cast<int>(rectangle
.Top());
1199 pNextClipRect
->right
= static_cast<int>(nRight
);
1200 pNextClipRect
->bottom
= static_cast<int>(nBottom
);
1205 mrParent
.mpClipRgnData
->rdh
.nCount
--;
1206 mrParent
.mpClipRgnData
->rdh
.nRgnSize
-= sizeof( RECT
);
1210 // create clip region from ClipRgnData
1211 if(0 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1213 // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1214 // that contains no polygons or only empty ones (no width/height). This is
1215 // perfectly fine and we are done, except setting it (see end of method)
1217 else if(1 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1219 RECT
* pRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1220 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
,
1221 pRect
->right
, pRect
->bottom
);
1223 else if(mrParent
.mpClipRgnData
->rdh
.nCount
> 1)
1225 sal_uLong nSize
= mrParent
.mpClipRgnData
->rdh
.nRgnSize
+sizeof(RGNDATAHEADER
);
1226 mrParent
.mhRegion
= ExtCreateRegion( nullptr, nSize
, mrParent
.mpClipRgnData
);
1228 // if ExtCreateRegion(...) is not supported
1229 if( !mrParent
.mhRegion
)
1231 RGNDATAHEADER
const & pHeader
= mrParent
.mpClipRgnData
->rdh
;
1233 if( pHeader
.nCount
)
1235 RECT
* pRect
= reinterpret_cast<RECT
*>(mrParent
.mpClipRgnData
->Buffer
);
1236 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
);
1239 for( sal_uLong n
= 1; n
< pHeader
.nCount
; n
++, pRect
++ )
1241 ScopedHRGN
hRgn(CreateRectRgn(pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
));
1242 CombineRgn( mrParent
.mhRegion
, mrParent
.mhRegion
, hRgn
.get(), RGN_OR
);
1247 if ( mrParent
.mpClipRgnData
!= mrParent
.mpStdClipRgnData
)
1248 delete [] reinterpret_cast<BYTE
*>(mrParent
.mpClipRgnData
);
1252 if( mrParent
.mhRegion
)
1254 SelectClipRgn( mrParent
.getHDC(), mrParent
.mhRegion
);
1256 // debug code if you want to check range of the newly applied ClipRegion
1258 //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1262 // #i123585# See above, this is a valid case, execute it
1263 SelectClipRgn( mrParent
.getHDC(), nullptr );
1267 void WinSalGraphicsImpl::SetLineColor()
1269 ResetPen(GetStockPen(NULL_PEN
));
1276 void WinSalGraphicsImpl::SetLineColor(Color nColor
)
1278 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1281 bool bStockPen
= false;
1283 HPEN hNewPen
= SearchStockPen(nPenColor
);
1287 hNewPen
= MakePen(nColor
);
1292 mnPenColor
= nPenColor
;
1293 maLineColor
= nColor
;
1295 mbStockPen
= bStockPen
;
1298 HPEN
WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor
)
1300 // Only screen, because printer has problems, when we use stock objects.
1301 if (!mrParent
.isPrinter())
1303 const SalData
* pSalData
= GetSalData();
1305 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockPenCount
; i
++)
1307 if (nPenColor
== pSalData
->maStockPenColorAry
[i
])
1308 return pSalData
->mhStockPenAry
[i
];
1315 HPEN
WinSalGraphicsImpl::MakePen(Color nColor
)
1317 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1321 if (!mrParent
.isPrinter())
1323 if (GetSalData()->mhDitherPal
&& ImplIsSysColorEntry(nColor
))
1325 nPenColor
= PALRGB_TO_RGB(nPenColor
);
1329 return CreatePen(PS_SOLID
, mrParent
.mnPenWidth
, nPenColor
);
1332 void WinSalGraphicsImpl::ResetPen(HPEN hNewPen
)
1334 HPEN hOldPen
= SelectPen(mrParent
.getHDC(), hNewPen
);
1345 mrParent
.mhDefPen
= hOldPen
;
1351 void WinSalGraphicsImpl::SetFillColor()
1353 ResetBrush(GetStockBrush(NULL_BRUSH
));
1357 mbStockBrush
= true;
1360 void WinSalGraphicsImpl::SetFillColor(Color nColor
)
1362 COLORREF nBrushColor
= PALETTERGB(nColor
.GetRed(),
1365 bool bStockBrush
= false;
1367 HBRUSH hNewBrush
= SearchStockBrush(nBrushColor
);
1371 hNewBrush
= MakeBrush(nColor
);
1373 ResetBrush(hNewBrush
);
1376 mnBrushColor
= nBrushColor
;
1377 maFillColor
= nColor
;
1379 mbStockBrush
= bStockBrush
;
1382 HBRUSH
WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor
)
1384 // Only screen, because printer has problems, when we use stock objects.
1385 if (!mrParent
.isPrinter())
1387 const SalData
* pSalData
= GetSalData();
1389 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockBrushCount
; i
++)
1391 if (nBrushColor
== pSalData
->maStockBrushColorAry
[i
])
1392 return pSalData
->mhStockBrushAry
[i
];
1402 BYTE
GetDitherMappingValue(BYTE nVal
, BYTE nThres
, const SalData
* pSalData
)
1404 return (pSalData
->mpDitherDiff
[nVal
] > nThres
) ?
1405 pSalData
->mpDitherHigh
[nVal
] : pSalData
->mpDitherLow
[nVal
];
1408 HBRUSH
Make16BitDIBPatternBrush(Color nColor
)
1410 const SalData
* pSalData
= GetSalData();
1412 const BYTE nRed
= nColor
.GetRed();
1413 const BYTE nGreen
= nColor
.GetGreen();
1414 const BYTE nBlue
= nColor
.GetBlue();
1416 static const BYTE aOrdDither16Bit
[8][8] =
1418 { 0, 6, 1, 7, 0, 6, 1, 7 },
1419 { 4, 2, 5, 3, 4, 2, 5, 3 },
1420 { 1, 7, 0, 6, 1, 7, 0, 6 },
1421 { 5, 3, 4, 2, 5, 3, 4, 2 },
1422 { 0, 6, 1, 7, 0, 6, 1, 7 },
1423 { 4, 2, 5, 3, 4, 2, 5, 3 },
1424 { 1, 7, 0, 6, 1, 7, 0, 6 },
1425 { 5, 3, 4, 2, 5, 3, 4, 2 }
1428 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1430 for(int nY
= 0; nY
< 8; ++nY
)
1432 for(int nX
= 0; nX
< 8; ++nX
)
1434 const BYTE nThres
= aOrdDither16Bit
[nY
][nX
];
1435 *pTmp
++ = GetDitherMappingValue(nBlue
, nThres
, pSalData
);
1436 *pTmp
++ = GetDitherMappingValue(nGreen
, nThres
, pSalData
);
1437 *pTmp
++ = GetDitherMappingValue(nRed
, nThres
, pSalData
);
1441 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_RGB_COLORS
);
1444 HBRUSH
Make8BitDIBPatternBrush(Color nColor
)
1446 const SalData
* pSalData
= GetSalData();
1448 const BYTE nRed
= nColor
.GetRed();
1449 const BYTE nGreen
= nColor
.GetGreen();
1450 const BYTE nBlue
= nColor
.GetBlue();
1452 static const BYTE aOrdDither8Bit
[8][8] =
1454 { 0, 38, 9, 48, 2, 40, 12, 50 },
1455 { 25, 12, 35, 22, 28, 15, 37, 24 },
1456 { 6, 44, 3, 41, 8, 47, 5, 44 },
1457 { 32, 19, 28, 16, 34, 21, 31, 18 },
1458 { 1, 40, 11, 49, 0, 39, 10, 48 },
1459 { 27, 14, 36, 24, 26, 13, 36, 23 },
1460 { 8, 46, 4, 43, 7, 45, 4, 42 },
1461 { 33, 20, 30, 17, 32, 20, 29, 16 }
1464 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1466 for (int nY
= 0; nY
< 8; ++nY
)
1468 for (int nX
= 0; nX
< 8; ++nX
)
1470 const BYTE nThres
= aOrdDither8Bit
[nY
][nX
];
1471 *pTmp
= GetDitherMappingValue(nRed
, nThres
, pSalData
) +
1472 GetDitherMappingValue(nGreen
, nThres
, pSalData
) * 6 +
1473 GetDitherMappingValue(nBlue
, nThres
, pSalData
) * 36;
1478 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_PAL_COLORS
);
1483 HBRUSH
WinSalGraphicsImpl::MakeBrush(Color nColor
)
1485 const SalData
* pSalData
= GetSalData();
1487 const BYTE nRed
= nColor
.GetRed();
1488 const BYTE nGreen
= nColor
.GetGreen();
1489 const BYTE nBlue
= nColor
.GetBlue();
1490 const COLORREF nBrushColor
= PALETTERGB(nRed
, nGreen
, nBlue
);
1492 if (mrParent
.isPrinter() || !pSalData
->mhDitherDIB
)
1493 return CreateSolidBrush(nBrushColor
);
1495 if (24 == reinterpret_cast<BITMAPINFOHEADER
*>(pSalData
->mpDitherDIB
)->biBitCount
)
1496 return Make16BitDIBPatternBrush(nColor
);
1498 if (ImplIsSysColorEntry(nColor
))
1499 return CreateSolidBrush(PALRGB_TO_RGB(nBrushColor
));
1501 if (ImplIsPaletteEntry(nRed
, nGreen
, nBlue
))
1502 return CreateSolidBrush(nBrushColor
);
1504 return Make8BitDIBPatternBrush(nColor
);
1507 void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush
)
1509 HBRUSH hOldBrush
= SelectBrush(mrParent
.getHDC(), hNewBrush
);
1515 DeleteBrush(mhBrush
);
1520 mrParent
.mhDefBrush
= hOldBrush
;
1523 mhBrush
= hNewBrush
;
1526 void WinSalGraphicsImpl::SetXORMode( bool bSet
, bool )
1529 ::SetROP2( mrParent
.getHDC(), bSet
? R2_XORPEN
: R2_COPYPEN
);
1532 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor
)
1534 SetLineColor( ImplGetROPColor( nROPColor
) );
1537 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor
)
1539 SetFillColor( ImplGetROPColor( nROPColor
) );
1542 void WinSalGraphicsImpl::DrawPixelImpl( tools::Long nX
, tools::Long nY
, COLORREF crColor
)
1544 const HDC hDC
= mrParent
.getHDC();
1548 SetPixel(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), crColor
);
1552 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(crColor
));
1553 PatBlt(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), int(1), int(1), PATINVERT
);
1556 void WinSalGraphicsImpl::drawPixel( tools::Long nX
, tools::Long nY
)
1558 DrawPixelImpl( nX
, nY
, mnPenColor
);
1561 void WinSalGraphicsImpl::drawPixel( tools::Long nX
, tools::Long nY
, Color nColor
)
1563 COLORREF nCol
= PALETTERGB( nColor
.GetRed(),
1567 if ( !mrParent
.isPrinter() &&
1568 GetSalData()->mhDitherPal
&&
1569 ImplIsSysColorEntry( nColor
) )
1570 nCol
= PALRGB_TO_RGB( nCol
);
1572 DrawPixelImpl( nX
, nY
, nCol
);
1575 void WinSalGraphicsImpl::drawLine( tools::Long nX1
, tools::Long nY1
, tools::Long nX2
, tools::Long nY2
)
1577 MoveToEx( mrParent
.getHDC(), static_cast<int>(nX1
), static_cast<int>(nY1
), nullptr );
1579 LineTo( mrParent
.getHDC(), static_cast<int>(nX2
), static_cast<int>(nY2
) );
1581 // LineTo doesn't draw the last pixel
1582 if ( !mrParent
.isPrinter() )
1583 DrawPixelImpl( nX2
, nY2
, mnPenColor
);
1586 void WinSalGraphicsImpl::drawRect( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
)
1590 if ( !mrParent
.isPrinter() )
1592 PatBlt( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nWidth
), static_cast<int>(nHeight
),
1593 mbXORMode
? PATINVERT
: PATCOPY
);
1600 aWinRect
.right
= nX
+nWidth
;
1601 aWinRect
.bottom
= nY
+nHeight
;
1602 ::FillRect( mrParent
.getHDC(), &aWinRect
, mhBrush
);
1606 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
1609 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints
, const Point
* pPtAry
)
1611 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1612 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1613 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1615 // for Windows 95 and its maximum number of points
1616 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1617 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1619 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1620 if ( !mrParent
.isPrinter() )
1621 DrawPixelImpl( pWinPtAry
[nPoints
-1].x
, pWinPtAry
[nPoints
-1].y
, mnPenColor
);
1624 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints
, const Point
* pPtAry
)
1626 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1627 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1628 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1630 // for Windows 95 and its maximum number of points
1631 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1632 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1635 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1636 const Point
** pPtAry
)
1638 UINT aWinPointAry
[SAL_POLYPOLYCOUNT_STACKBUF
];
1640 UINT nPolyPolyPoints
= 0;
1644 if ( nPoly
<= SAL_POLYPOLYCOUNT_STACKBUF
)
1645 pWinPointAry
= aWinPointAry
;
1647 pWinPointAry
= new UINT
[nPoly
];
1649 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1651 nPoints
= static_cast<UINT
>(pPoints
[i
])+1;
1652 pWinPointAry
[i
] = nPoints
;
1653 nPolyPolyPoints
+= nPoints
;
1656 POINT aWinPointAryAry
[SAL_POLYPOLYPOINTS_STACKBUF
];
1657 POINT
* pWinPointAryAry
;
1658 if ( nPolyPolyPoints
<= SAL_POLYPOLYPOINTS_STACKBUF
)
1659 pWinPointAryAry
= aWinPointAryAry
;
1661 pWinPointAryAry
= new POINT
[nPolyPolyPoints
];
1663 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1665 nPoints
= pWinPointAry
[i
];
1666 const Point
* pPolyAry
= pPtAry
[i
];
1667 for (sal_uInt32 j
=0; j
<nPoints
-1; ++j
)
1668 pWinPointAryAry
[n
+j
] = POINT
{ static_cast<LONG
>(pPolyAry
[j
].getX()), static_cast<LONG
>(pPolyAry
[j
].getY()) };
1669 pWinPointAryAry
[n
+nPoints
-1] = pWinPointAryAry
[n
];
1673 if ( !PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), static_cast<UINT
>(nPoly
) ) &&
1674 (nPolyPolyPoints
> MAX_64KSALPOINTS
) )
1676 nPolyPolyPoints
= 0;
1680 nPolyPolyPoints
+= pWinPointAry
[static_cast<UINT
>(nPoly
)];
1683 while ( nPolyPolyPoints
< MAX_64KSALPOINTS
);
1685 if ( pWinPointAry
[static_cast<UINT
>(nPoly
)] > MAX_64KSALPOINTS
)
1686 pWinPointAry
[static_cast<UINT
>(nPoly
)] = MAX_64KSALPOINTS
;
1688 Polygon( mrParent
.getHDC(), pWinPointAryAry
, *pWinPointAry
);
1690 PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), nPoly
);
1693 if ( pWinPointAry
!= aWinPointAry
)
1694 delete [] pWinPointAry
;
1695 if ( pWinPointAryAry
!= aWinPointAryAry
)
1696 delete [] pWinPointAryAry
;
1699 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1701 // #100127# draw an array of points which might also contain bezier control points
1705 const HDC hdc
= mrParent
.getHDC();
1707 // TODO: profile whether the following options are faster:
1708 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1709 // b) convert our flag array to window's and use PolyDraw
1710 MoveToEx(hdc
, static_cast<LONG
>(pPtAry
->getX()), static_cast<LONG
>(pPtAry
->getY()), nullptr);
1714 for(sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1716 if(*pFlgAry
!= PolyFlags::Control
)
1718 LineTo(hdc
, pPtAry
->getX(), pPtAry
->getY());
1720 else if(nPoints
- i
> 2)
1722 POINT bezierPoints
[] = {
1723 POINT
{ static_cast<LONG
>(pPtAry
[0].getX()), static_cast<LONG
>(pPtAry
[0].getY()) },
1724 POINT
{ static_cast<LONG
>(pPtAry
[1].getX()), static_cast<LONG
>(pPtAry
[1].getY()) },
1725 POINT
{ static_cast<LONG
>(pPtAry
[2].getX()), static_cast<LONG
>(pPtAry
[2].getY()) },
1727 PolyBezierTo(hdc
, bezierPoints
, 3);
1740 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1742 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1743 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1744 POINT
* pWinPointAry
;
1746 if( nPoints
> SAL_POLY_STACKBUF
)
1748 pWinPointAry
= new POINT
[ nPoints
];
1749 pWinFlagAry
= new BYTE
[ nPoints
];
1753 pWinPointAry
= aStackAry1
;
1754 pWinFlagAry
= aStackAry2
;
1757 sal_uInt32
nPoints_i32(nPoints
);
1758 ImplPreparePolyDraw(true, 1, &nPoints_i32
, &pPtAry
, &pFlgAry
, pWinPointAry
, pWinFlagAry
);
1762 if( BeginPath( mrParent
.getHDC() ) )
1764 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nPoints
);
1766 if( EndPath( mrParent
.getHDC() ) )
1768 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1773 if( pWinPointAry
!= aStackAry1
)
1775 delete [] pWinPointAry
;
1776 delete [] pWinFlagAry
;
1782 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1783 const Point
* const* pPtAry
, const PolyFlags
* const* pFlgAry
)
1785 sal_uLong nCurrPoly
, nTotalPoints
;
1786 const sal_uInt32
* pCurrPoints
= pPoints
;
1787 for( nCurrPoly
=0, nTotalPoints
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
1788 nTotalPoints
+= *pCurrPoints
++;
1790 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1791 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1792 POINT
* pWinPointAry
;
1794 if( nTotalPoints
> SAL_POLY_STACKBUF
)
1796 pWinPointAry
= new POINT
[ nTotalPoints
];
1797 pWinFlagAry
= new BYTE
[ nTotalPoints
];
1801 pWinPointAry
= aStackAry1
;
1802 pWinFlagAry
= aStackAry2
;
1805 ImplPreparePolyDraw(true, nPoly
, pPoints
, pPtAry
, pFlgAry
, pWinPointAry
, pWinFlagAry
);
1809 if( BeginPath( mrParent
.getHDC() ) )
1811 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nTotalPoints
);
1813 if( EndPath( mrParent
.getHDC() ) )
1815 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1820 if( pWinPointAry
!= aStackAry1
)
1822 delete [] pWinPointAry
;
1823 delete [] pWinFlagAry
;
1829 static basegfx::B2DPoint
impPixelSnap(
1830 const basegfx::B2DPolygon
& rPolygon
,
1831 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1832 basegfx::B2DHomMatrix
& rObjectToDeviceInv
,
1835 const sal_uInt32
nCount(rPolygon
.count());
1838 const basegfx::B2ITuple
aPrevTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ nCount
- 1) % nCount
)));
1839 const basegfx::B2DPoint
aCurrPoint(rObjectToDevice
* rPolygon
.getB2DPoint(nIndex
));
1840 const basegfx::B2ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1841 const basegfx::B2ITuple
aNextTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ 1) % nCount
)));
1844 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1845 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1846 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1847 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1848 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1849 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1851 if(bSnapX
|| bSnapY
)
1853 basegfx::B2DPoint
aSnappedPoint(
1854 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1855 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY());
1857 if(rObjectToDeviceInv
.isIdentity())
1859 rObjectToDeviceInv
= rObjectToDevice
;
1860 rObjectToDeviceInv
.invert();
1863 aSnappedPoint
*= rObjectToDeviceInv
;
1865 return aSnappedPoint
;
1868 return rPolygon
.getB2DPoint(nIndex
);
1871 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1872 Gdiplus::GraphicsPath
& rGraphicsPath
,
1873 const basegfx::B2DPolygon
& rPolygon
,
1874 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1876 bool bPixelSnapHairline
)
1878 sal_uInt32
nCount(rPolygon
.count());
1882 const sal_uInt32
nEdgeCount(rPolygon
.isClosed() ? nCount
: nCount
- 1);
1886 const bool bControls(rPolygon
.areControlPointsUsed());
1887 basegfx::B2DPoint
aCurr(rPolygon
.getB2DPoint(0));
1888 basegfx::B2DHomMatrix aObjectToDeviceInv
;
1890 if(bPixelSnapHairline
)
1892 aCurr
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, 0);
1895 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1897 const sal_uInt32
nNextIndex((a
+ 1) % nCount
);
1898 basegfx::B2DPoint
aNext(rPolygon
.getB2DPoint(nNextIndex
));
1899 const bool b1stControlPointUsed(bControls
&& rPolygon
.isNextControlPointUsed(a
));
1900 const bool b2ndControlPointUsed(bControls
&& rPolygon
.isPrevControlPointUsed(nNextIndex
));
1902 if(bPixelSnapHairline
)
1904 aNext
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, nNextIndex
);
1907 if(b1stControlPointUsed
|| b2ndControlPointUsed
)
1909 basegfx::B2DPoint
aCa(rPolygon
.getNextControlPoint(a
));
1910 basegfx::B2DPoint
aCb(rPolygon
.getPrevControlPoint(nNextIndex
));
1912 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1913 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1914 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1915 // (basegfx can handle that).
1916 // Caution: This error (and it's correction) might be necessary for other graphical
1917 // sub-systems in a similar way.
1918 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1919 // vector was wrong. Best alternative is one as close as possible which means short.
1920 if(!b1stControlPointUsed
)
1922 aCa
= aCurr
+ ((aCb
- aCurr
) * 0.0005);
1924 else if(!b2ndControlPointUsed
)
1926 aCb
= aNext
+ ((aCa
- aNext
) * 0.0005);
1929 rGraphicsPath
.AddBezier(
1930 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1931 static_cast< Gdiplus::REAL
>(aCa
.getX()), static_cast< Gdiplus::REAL
>(aCa
.getY()),
1932 static_cast< Gdiplus::REAL
>(aCb
.getX()), static_cast< Gdiplus::REAL
>(aCb
.getY()),
1933 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1937 rGraphicsPath
.AddLine(
1938 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1939 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1942 if(a
+ 1 < nEdgeCount
)
1948 rGraphicsPath
.StartFigure();
1958 class SystemDependentData_GraphicsPath
: public basegfx::SystemDependentData
1961 // the path data itself
1962 std::shared_ptr
<Gdiplus::GraphicsPath
> mpGraphicsPath
;
1964 // all other values the triangulation is based on and
1965 // need to be compared with to check for data validity
1967 std::vector
< double > maStroke
;
1970 SystemDependentData_GraphicsPath(
1971 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
1973 const std::vector
< double >* pStroke
); // MM01
1976 std::shared_ptr
<Gdiplus::GraphicsPath
>& getGraphicsPath() { return mpGraphicsPath
; }
1977 bool getNoLineJoin() const { return mbNoLineJoin
; }
1978 const std::vector
< double >& getStroke() const { return maStroke
; }
1980 virtual sal_Int64
estimateUsageInBytes() const override
;
1985 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
1986 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
1988 const std::vector
< double >* pStroke
)
1989 : basegfx::SystemDependentData(
1990 Application::GetSystemDependentDataManager(),
1991 basegfx::SDD_Type::SDDType_GraphicsPath
),
1992 mpGraphicsPath(rpGraphicsPath
),
1993 mbNoLineJoin(bNoLineJoin
),
1996 if(nullptr != pStroke
)
1998 maStroke
= *pStroke
;
2002 sal_Int64
SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2004 sal_Int64
nRetval(0);
2008 const INT
nPointCount(mpGraphicsPath
->GetPointCount());
2010 if(0 != nPointCount
)
2013 // - 2 x sizeof(Gdiplus::REAL)
2014 // - 1 byte (see GetPathTypes in docu)
2015 nRetval
= nPointCount
* ((2 * sizeof(Gdiplus::REAL
)) + 1);
2022 void WinSalGraphicsImpl::drawPolyPolygon(
2023 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2024 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
2025 double fTransparency
)
2027 const sal_uInt32
nCount(rPolyPolygon
.count());
2029 if(!mbBrush
|| 0 == nCount
|| fTransparency
< 0.0 || fTransparency
> 1.0)
2034 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2035 const sal_uInt8
aTrans(sal_uInt8(255) - static_cast<sal_uInt8
>(basegfx::fround(fTransparency
* 255.0)));
2036 const Gdiplus::Color
aTestColor(aTrans
, maFillColor
.GetRed(), maFillColor
.GetGreen(), maFillColor
.GetBlue());
2037 const Gdiplus::SolidBrush
aSolidBrush(aTestColor
.GetValue());
2039 // Set full (Object-to-Device) transformation - if used
2040 if(rObjectToDevice
.isIdentity())
2042 aGraphics
.ResetTransform();
2046 Gdiplus::Matrix aMatrix
;
2048 aMatrix
.SetElements(
2049 rObjectToDevice
.get(0, 0),
2050 rObjectToDevice
.get(1, 0),
2051 rObjectToDevice
.get(0, 1),
2052 rObjectToDevice
.get(1, 1),
2053 rObjectToDevice
.get(0, 2),
2054 rObjectToDevice
.get(1, 2));
2055 aGraphics
.SetTransform(&aMatrix
);
2058 // prepare local instance of Gdiplus::GraphicsPath
2059 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2061 // try to access buffered data
2062 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2063 rPolyPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>(basegfx::SDD_Type::SDDType_GraphicsPath
));
2065 if(pSystemDependentData_GraphicsPath
)
2067 // copy buffered data
2068 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2072 // Note: In principle we could use the same buffered geometry at line
2073 // and fill polygons. Checked that in a first try, used
2074 // GraphicsPath::AddPath from Gdiplus combined with below used
2075 // StartFigure/CloseFigure, worked well (thus the line-draw version
2076 // may create non-closed partial Polygon data).
2078 // But in current reality it gets not used due to e.g.
2079 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2080 // line and fill polygon-primitives (what could be changed).
2082 // There will probably be more hindrances here in other rendering paths
2083 // which could all be found - intention to do this would be: Use more
2084 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2086 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2087 // and embed into a TransformPrimitive2D containing the transformation.
2089 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2090 // && !bIsHairline) creates polygon fill infos that are not reusable
2091 // for the fill case (see ::drawPolyLine below) - thus we would need a
2092 // bool and/or two system-dependent paths buffered - doable, but complicated.
2094 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2095 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2096 // (at least for now...)
2099 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2101 for(sal_uInt32
a(0); a
< nCount
; a
++)
2105 // #i101491# not needed for first run
2106 pGraphicsPath
->StartFigure();
2109 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2111 rPolyPolygon
.getB2DPolygon(a
),
2112 rObjectToDevice
, // not used due to the two 'false' values below, but to not forget later
2116 pGraphicsPath
->CloseFigure();
2119 // add to buffering mechanism
2120 rPolyPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2126 if(mrParent
.getAntiAlias())
2128 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2132 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2135 if(mrParent
.isPrinter())
2138 // Normally GdiPlus should not be used for printing at all since printers cannot
2139 // print transparent filled polygon geometry and normally this does not happen
2140 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2141 // and no transparent parts should remain for printing. But this can be overridden
2142 // by the user and thus happens. This call can only come (currently) from
2143 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2144 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2145 // checked that there is *no* transformation set and estimated that a stable factor
2146 // dependent of the printer's DPI is used. Create and set a transformation here to
2148 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2149 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2151 // Now the transformation maybe/is already used (see above), so do
2152 // modify it without resetting to not destroy it.
2153 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2154 // we need - in our notation, would be a multiply from left to execute
2155 // current transform first and this scale last.
2156 // I tried to trigger this code using Print from the menu and various
2157 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2158 // more, feel free to remove it.
2159 // One more hint: This *may* also be needed now in ::drawPolyLine below
2160 // since it also uses transformations now.
2162 // aGraphics.ResetTransform();
2164 aGraphics
.ScaleTransform(
2165 Gdiplus::REAL(100.0) / aDpiX
,
2166 Gdiplus::REAL(100.0) / aDpiY
,
2167 Gdiplus::MatrixOrderAppend
);
2170 // use created or buffered data
2176 bool WinSalGraphicsImpl::drawPolyLine(
2177 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2178 const basegfx::B2DPolygon
& rPolygon
,
2179 double fTransparency
,
2181 const std::vector
< double >* pStroke
, // MM01
2182 basegfx::B2DLineJoin eLineJoin
,
2183 css::drawing::LineCap eLineCap
,
2184 double fMiterMinimumAngle
,
2185 bool bPixelSnapHairline
)
2187 // MM01 check done for simple reasons
2188 if(!mbPen
|| !rPolygon
.count() || fTransparency
< 0.0 || fTransparency
> 1.0)
2193 // need to check/handle LineWidth when ObjectToDevice transformation is used
2194 const bool bObjectToDeviceIsIdentity(rObjectToDevice
.isIdentity());
2195 const bool bIsHairline(fLineWidth
== 0);
2197 // tdf#124848 calculate-back logical LineWidth for a hairline
2198 // since this implementation hands over the transformation to
2199 // the graphic sub-system
2204 if(!bObjectToDeviceIsIdentity
)
2206 basegfx::B2DHomMatrix
aObjectToDeviceInv(rObjectToDevice
);
2207 aObjectToDeviceInv
.invert();
2208 fLineWidth
= (aObjectToDeviceInv
* basegfx::B2DVector(fLineWidth
, 0)).getLength();
2212 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2213 const sal_uInt8 aTrans
= static_cast<sal_uInt8
>(basegfx::fround( 255 * (1.0 - fTransparency
) ));
2214 const Gdiplus::Color
aTestColor(aTrans
, maLineColor
.GetRed(), maLineColor
.GetGreen(), maLineColor
.GetBlue());
2215 Gdiplus::Pen
aPen(aTestColor
.GetValue(), Gdiplus::REAL(fLineWidth
));
2216 bool bNoLineJoin(false);
2218 // Set full (Object-to-Device) transformation - if used
2219 if(bObjectToDeviceIsIdentity
)
2221 aGraphics
.ResetTransform();
2225 Gdiplus::Matrix aMatrix
;
2227 aMatrix
.SetElements(
2228 rObjectToDevice
.get(0, 0),
2229 rObjectToDevice
.get(1, 0),
2230 rObjectToDevice
.get(0, 1),
2231 rObjectToDevice
.get(1, 1),
2232 rObjectToDevice
.get(0, 2),
2233 rObjectToDevice
.get(1, 2));
2234 aGraphics
.SetTransform(&aMatrix
);
2239 case basegfx::B2DLineJoin::NONE
:
2247 case basegfx::B2DLineJoin::Bevel
:
2249 aPen
.SetLineJoin(Gdiplus::LineJoinBevel
);
2252 case basegfx::B2DLineJoin::Miter
:
2254 const Gdiplus::REAL
aMiterLimit(1.0/sin(fMiterMinimumAngle
/2.0));
2256 aPen
.SetMiterLimit(aMiterLimit
);
2257 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2258 // graphics, somewhere clipped in some distance from the edge point, dependent
2259 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2261 aPen
.SetLineJoin(Gdiplus::LineJoinMiterClipped
);
2264 case basegfx::B2DLineJoin::Round
:
2266 aPen
.SetLineJoin(Gdiplus::LineJoinRound
);
2273 default: /*css::drawing::LineCap_BUTT*/
2278 case css::drawing::LineCap_ROUND
:
2280 aPen
.SetStartCap(Gdiplus::LineCapRound
);
2281 aPen
.SetEndCap(Gdiplus::LineCapRound
);
2284 case css::drawing::LineCap_SQUARE
:
2286 aPen
.SetStartCap(Gdiplus::LineCapSquare
);
2287 aPen
.SetEndCap(Gdiplus::LineCapSquare
);
2292 // prepare local instance of Gdiplus::GraphicsPath
2293 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2295 // try to access buffered data
2296 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2297 rPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>(basegfx::SDD_Type::SDDType_GraphicsPath
));
2299 // MM01 need to do line dashing as fallback stuff here now
2300 const double fDotDashLength(nullptr != pStroke
? std::accumulate(pStroke
->begin(), pStroke
->end(), 0.0) : 0.0);
2301 const bool bStrokeUsed(0.0 != fDotDashLength
);
2302 assert(!bStrokeUsed
|| (bStrokeUsed
&& pStroke
));
2304 // MM01 decide if to stroke directly
2305 static bool bDoDirectGDIPlusStroke(true);
2307 // activate to stroke directly
2308 if(bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2310 // tdf#124848 the fix of tdf#130478 that was needed here before
2311 // gets much easier when already handling the hairline case above,
2312 // the back-calculated logical linewidth is already here, just use it.
2313 // Still be careful - a zero LineWidth *should* not happen, but...
2314 std::vector
<Gdiplus::REAL
> aDashArray(pStroke
->size());
2315 const double fFactor(fLineWidth
== 0 ? 1.0 : 1.0 / fLineWidth
);
2317 // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
2318 // dash or dot themselves. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
2319 // and other renders (e.g. Impress slide show), while keeping the total length of the
2321 // Patterns are always a sequence dash space dash space ...
2322 if (eLineCap
!= css::drawing::LineCap_BUTT
)
2324 size_t nSize
= pStroke
->size();
2325 // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
2328 for(size_t a(0); a
< nSize
; a
++)
2330 double fDashLengthRel
= (*pStroke
)[2 * a
] * fFactor
;
2331 double fSpaceLengthRel
= (*pStroke
)[2 * a
+ 1] * fFactor
;
2332 // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
2333 // appearance is different, in case space is too small.
2334 double fCorrect
= fSpaceLengthRel
- 1.0 <= 0 ? fSpaceLengthRel
- 0.01 : 1.0;
2335 aDashArray
[2 * a
] = Gdiplus::REAL(fDashLengthRel
+ fCorrect
);
2336 aDashArray
[2 * a
+ 1] = Gdiplus::REAL(fSpaceLengthRel
- fCorrect
);
2341 for(size_t a(0); a
< pStroke
->size(); a
++)
2343 aDashArray
[a
] = Gdiplus::REAL((*pStroke
)[a
] * fFactor
);
2346 if (eLineCap
== css::drawing::LineCap_ROUND
)
2347 aPen
.SetDashCap(Gdiplus::DashCapRound
);
2349 aPen
.SetDashCap(Gdiplus::DashCapFlat
); // "square" doesn't exist in Gdiplus
2350 aPen
.SetDashOffset(Gdiplus::REAL(0.0));
2351 aPen
.SetDashPattern(aDashArray
.data(), aDashArray
.size());
2354 if(!bDoDirectGDIPlusStroke
&& pSystemDependentData_GraphicsPath
)
2356 // MM01 - check on stroke change. Used against not used, or if oth used,
2357 // equal or different? Triangulation geometry creation depends heavily
2358 // on stroke, independent of being transformation independent
2359 const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath
->getStroke().empty());
2361 if(bStrokeWasUsed
!= bStrokeUsed
2362 || (bStrokeUsed
&& *pStroke
!= pSystemDependentData_GraphicsPath
->getStroke()))
2364 // data invalid, forget
2365 pSystemDependentData_GraphicsPath
.reset();
2369 if(pSystemDependentData_GraphicsPath
)
2371 // check data validity
2372 if (pSystemDependentData_GraphicsPath
->getNoLineJoin() != bNoLineJoin
2373 || bPixelSnapHairline
/*tdf#124700*/)
2375 // data invalid, forget
2376 pSystemDependentData_GraphicsPath
.reset();
2380 if(pSystemDependentData_GraphicsPath
)
2382 // copy buffered data
2383 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2387 // fill data of buffered data
2388 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2390 if(!bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2392 // MM01 need to do line dashing as fallback stuff here now
2393 basegfx::B2DPolyPolygon aPolyPolygonLine
;
2396 basegfx::utils::applyLineDashing(
2398 *pStroke
, // pattern
2399 &aPolyPolygonLine
, // target for lines
2400 nullptr, // target for gaps
2401 fDotDashLength
); // full length if available
2403 // MM01 checked/verified, ok
2404 for(sal_uInt32
a(0); a
< aPolyPolygonLine
.count(); a
++)
2406 const basegfx::B2DPolygon
aPolyLine(aPolyPolygonLine
.getB2DPolygon(a
));
2407 pGraphicsPath
->StartFigure();
2408 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2413 bPixelSnapHairline
);
2418 // no line dashing or direct stroke, just copy
2419 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2424 bPixelSnapHairline
);
2426 if(rPolygon
.isClosed() && !bNoLineJoin
)
2428 // #i101491# needed to create the correct line joins
2429 pGraphicsPath
->CloseFigure();
2433 // add to buffering mechanism
2434 if (!bPixelSnapHairline
/*tdf#124700*/)
2436 rPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2443 if(mrParent
.getAntiAlias())
2445 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2449 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2452 if(mrParent
.isPrinter())
2454 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2455 // (look for 'One more hint: This *may* also be needed now in'...).
2456 // See comments in same spot above *urgently* before doing changes here,
2457 // these comments are *still fully valid* at this place (!)
2458 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2459 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2461 aGraphics
.ScaleTransform(
2462 Gdiplus::REAL(100.0) / aDpiX
,
2463 Gdiplus::REAL(100.0) / aDpiY
,
2464 Gdiplus::MatrixOrderAppend
);
2474 static void paintToGdiPlus(
2475 Gdiplus::Graphics
& rGraphics
,
2476 const SalTwoRect
& rTR
,
2477 Gdiplus::Bitmap
& rBitmap
)
2479 // only parts of source are used
2480 Gdiplus::PointF aDestPoints
[3];
2481 Gdiplus::ImageAttributes aAttributes
;
2483 // define target region as parallelogram
2484 aDestPoints
[0].X
= Gdiplus::REAL(rTR
.mnDestX
);
2485 aDestPoints
[0].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2486 aDestPoints
[1].X
= Gdiplus::REAL(rTR
.mnDestX
+ rTR
.mnDestWidth
);
2487 aDestPoints
[1].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2488 aDestPoints
[2].X
= Gdiplus::REAL(rTR
.mnDestX
);
2489 aDestPoints
[2].Y
= Gdiplus::REAL(rTR
.mnDestY
+ rTR
.mnDestHeight
);
2491 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2493 rGraphics
.DrawImage(
2497 Gdiplus::REAL(rTR
.mnSrcX
),
2498 Gdiplus::REAL(rTR
.mnSrcY
),
2499 Gdiplus::REAL(rTR
.mnSrcWidth
),
2500 Gdiplus::REAL(rTR
.mnSrcHeight
),
2505 static void setInterpolationMode(
2506 Gdiplus::Graphics
& rGraphics
,
2507 tools::Long rSrcWidth
,
2508 tools::Long rDestWidth
,
2509 tools::Long rSrcHeight
,
2510 tools::Long rDestHeight
)
2512 const bool bSameWidth(rSrcWidth
== rDestWidth
);
2513 const bool bSameHeight(rSrcHeight
== rDestHeight
);
2515 if(bSameWidth
&& bSameHeight
)
2517 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeInvalid
);
2519 else if(rDestWidth
> rSrcWidth
&& rDestHeight
> rSrcHeight
)
2521 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2523 else if(rDestWidth
< rSrcWidth
&& rDestHeight
< rSrcHeight
)
2525 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeBicubic
);
2529 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2533 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect
& rTR
, const SalBitmap
& rSrcBitmap
)
2535 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2537 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2539 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2540 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap());
2544 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2546 setInterpolationMode(
2565 bool WinSalGraphicsImpl::blendBitmap(
2572 bool WinSalGraphicsImpl::blendAlphaBitmap(
2581 bool WinSalGraphicsImpl::drawAlphaBitmap(
2582 const SalTwoRect
& rTR
,
2583 const SalBitmap
& rSrcBitmap
,
2584 const SalBitmap
& rAlphaBmp
)
2586 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2588 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2589 assert(dynamic_cast<const WinSalBitmap
*>(&rAlphaBmp
));
2591 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2592 const WinSalBitmap
& rSalAlpha
= static_cast< const WinSalBitmap
& >(rAlphaBmp
);
2593 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(&rSalAlpha
));
2597 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2599 setInterpolationMode(
2618 bool WinSalGraphicsImpl::drawTransformedBitmap(
2619 const basegfx::B2DPoint
& rNull
,
2620 const basegfx::B2DPoint
& rX
,
2621 const basegfx::B2DPoint
& rY
,
2622 const SalBitmap
& rSourceBitmap
,
2623 const SalBitmap
* pAlphaBitmap
,
2626 assert(dynamic_cast<const WinSalBitmap
*>(&rSourceBitmap
));
2627 assert(!pAlphaBitmap
|| dynamic_cast<const WinSalBitmap
*>(pAlphaBitmap
));
2632 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSourceBitmap
);
2633 const WinSalBitmap
* pSalAlpha
= static_cast< const WinSalBitmap
* >(pAlphaBitmap
);
2634 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(pSalAlpha
));
2638 const tools::Long
nSrcWidth(aARGB
->GetWidth());
2639 const tools::Long
nSrcHeight(aARGB
->GetHeight());
2641 if(nSrcWidth
&& nSrcHeight
)
2643 const tools::Long
nDestWidth(basegfx::fround
<tools::Long
>(basegfx::B2DVector(rX
- rNull
).getLength()));
2644 const tools::Long
nDestHeight(basegfx::fround
<tools::Long
>(basegfx::B2DVector(rY
- rNull
).getLength()));
2646 if(nDestWidth
&& nDestHeight
)
2648 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2649 Gdiplus::PointF aDestPoints
[3];
2650 Gdiplus::ImageAttributes aAttributes
;
2652 setInterpolationMode(
2659 // this mode is only capable of drawing the whole bitmap to a parallelogram
2660 aDestPoints
[0].X
= Gdiplus::REAL(rNull
.getX());
2661 aDestPoints
[0].Y
= Gdiplus::REAL(rNull
.getY());
2662 aDestPoints
[1].X
= Gdiplus::REAL(rX
.getX());
2663 aDestPoints
[1].Y
= Gdiplus::REAL(rX
.getY());
2664 aDestPoints
[2].X
= Gdiplus::REAL(rY
.getX());
2665 aDestPoints
[2].Y
= Gdiplus::REAL(rY
.getY());
2667 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2669 aGraphics
.DrawImage(
2675 Gdiplus::REAL(nSrcWidth
),
2676 Gdiplus::REAL(nSrcHeight
),
2688 bool WinSalGraphicsImpl::hasFastDrawTransformedBitmap() const
2693 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon
& /*rPolygon*/,
2694 const Gradient
& /*rGradient*/)
2699 bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon
const & /*rPolyPolygon*/,
2700 SalGradient
const & /*rGradient*/)
2705 bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType
/*eType*/) const
2710 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */