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 .
22 #include "gdiimpl.hxx"
25 #include <rtl/strbuf.hxx>
26 #include <sal/log.hxx>
27 #include <tools/poly.hxx>
28 #include <basegfx/polygon/b2dpolygon.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <win/wincomp.hxx>
32 #include <win/saldata.hxx>
33 #include <win/salgdi.h>
34 #include <win/salbmp.h>
35 #include <win/scoped_gdi.hxx>
36 #include <vcl/BitmapAccessMode.hxx>
37 #include <vcl/BitmapBuffer.hxx>
38 #include <vcl/BitmapPalette.hxx>
39 #include <win/salframe.h>
40 #include <basegfx/matrix/b2dhommatrixtools.hxx>
41 #include <basegfx/utils/systemdependentdata.hxx>
43 #include <outdata.hxx>
44 #include <win/salids.hrc>
45 #include <ControlCacheKey.hxx>
49 #define min(a,b) (((a) < (b)) ? (a) : (b))
52 #define max(a,b) (((a) > (b)) ? (a) : (b))
59 #include <gdiplusenums.h>
60 #include <gdipluscolor.h>
64 #define SAL_POLYPOLYCOUNT_STACKBUF 8
65 #define SAL_POLYPOLYPOINTS_STACKBUF 64
67 #define SAL_POLY_STACKBUF 32
71 // #100127# Fill point and flag memory from array of points which
72 // might also contain bezier control points for the PolyDraw() GDI method
73 // Make sure pWinPointAry and pWinFlagAry are big enough
74 void ImplPreparePolyDraw( bool bCloseFigures
,
76 const sal_uInt32
* pPoints
,
77 const SalPoint
* const* pPtAry
,
78 const PolyFlags
* const* pFlgAry
,
83 for( nCurrPoly
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
85 const POINT
* pCurrPoint
= reinterpret_cast<const POINT
*>( *pPtAry
++ );
86 const PolyFlags
* pCurrFlag
= *pFlgAry
++;
87 const sal_uInt32 nCurrPoints
= *pPoints
++;
88 const bool bHaveFlagArray( pCurrFlag
);
94 *pWinPointAry
++ = *pCurrPoint
++;
95 *pWinFlagAry
++ = PT_MOVETO
;
98 for( nCurrPoint
=1; nCurrPoint
<nCurrPoints
; )
100 // #102067# Check existence of flag array
101 if( bHaveFlagArray
&&
102 ( nCurrPoint
+ 2 ) < nCurrPoints
)
104 PolyFlags
P4( pCurrFlag
[ 2 ] );
106 if( ( PolyFlags::Control
== pCurrFlag
[ 0 ] ) &&
107 ( PolyFlags::Control
== pCurrFlag
[ 1 ] ) &&
108 ( PolyFlags::Normal
== P4
|| PolyFlags::Smooth
== P4
|| PolyFlags::Symmetric
== P4
) )
111 *pWinPointAry
++ = *pCurrPoint
++;
112 *pWinFlagAry
++ = PT_BEZIERTO
;
115 *pWinPointAry
++ = *pCurrPoint
++;
116 *pWinFlagAry
++ = PT_BEZIERTO
;
119 *pWinPointAry
++ = *pCurrPoint
++;
120 *pWinFlagAry
++ = PT_BEZIERTO
;
128 // regular line point
129 *pWinPointAry
++ = *pCurrPoint
++;
130 *pWinFlagAry
++ = PT_LINETO
;
137 pWinFlagAry
[-1] |= PT_CLOSEFIGURE
;
142 Color
ImplGetROPColor( SalROPColor nROPColor
)
145 if ( nROPColor
== SalROPColor::N0
)
146 nColor
= Color( 0, 0, 0 );
148 nColor
= Color( 255, 255, 255 );
152 bool IsDitherColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
154 constexpr sal_uInt8 DITHER_PAL_DELTA
= 51;
156 return !(nRed
% DITHER_PAL_DELTA
) &&
157 !(nGreen
% DITHER_PAL_DELTA
) &&
158 !(nBlue
% DITHER_PAL_DELTA
);
161 bool IsPaletteColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
163 static const PALETTEENTRY aImplSalSysPalEntryAry
[] =
168 { 0, 0x80, 0x80, 0 },
170 { 0x80, 0, 0x80, 0 },
171 { 0x80, 0x80, 0, 0 },
172 { 0x80, 0x80, 0x80, 0 },
173 { 0xC0, 0xC0, 0xC0, 0 },
176 { 0, 0xFF, 0xFF, 0 },
178 { 0xFF, 0, 0xFF, 0 },
179 { 0xFF, 0xFF, 0, 0 },
180 { 0xFF, 0xFF, 0xFF, 0 }
183 for (const auto& rPalEntry
: aImplSalSysPalEntryAry
)
185 if(rPalEntry
.peRed
== nRed
&&
186 rPalEntry
.peGreen
== nGreen
&&
187 rPalEntry
.peBlue
== nBlue
)
196 bool IsExtraColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
198 return (nRed
== 0) && (nGreen
== 184) && (nBlue
== 255);
201 bool ImplIsPaletteEntry(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
203 return IsDitherColor(nRed
, nGreen
, nBlue
) ||
204 IsPaletteColor(nRed
, nGreen
, nBlue
) ||
205 IsExtraColor(nRed
, nGreen
, nBlue
);
210 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics
& rParent
):
222 WinSalGraphicsImpl::~WinSalGraphicsImpl()
233 DeleteBrush( mhBrush
);
237 void WinSalGraphicsImpl::Init()
241 void WinSalGraphicsImpl::freeResources()
245 bool WinSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uInt32
)
250 void WinSalGraphicsImpl::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
256 hSrcDC
= static_cast<WinSalGraphics
*>(pSrcGraphics
)->getHDC();
258 hSrcDC
= mrParent
.getHDC();
265 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
266 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
268 BitBlt( mrParent
.getHDC(),
269 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
270 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
272 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
277 int nOldStretchMode
= SetStretchBltMode( mrParent
.getHDC(), STRETCH_DELETESCANS
);
278 StretchBlt( mrParent
.getHDC(),
279 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
280 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
282 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
283 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
285 SetStretchBltMode( mrParent
.getHDC(), nOldStretchMode
);
292 void MakeInvisibleArea(const RECT
& rSrcRect
,
293 int nLeft
, int nTop
, int nRight
, int nBottom
,
294 HRGN
& rhInvalidateRgn
)
296 if (!rhInvalidateRgn
)
298 rhInvalidateRgn
= CreateRectRgnIndirect(&rSrcRect
);
301 ScopedHRGN
hTempRgn(CreateRectRgn(nLeft
, nTop
, nRight
, nBottom
));
302 CombineRgn(rhInvalidateRgn
, rhInvalidateRgn
, hTempRgn
.get(), RGN_DIFF
);
305 void ImplCalcOutSideRgn( const RECT
& rSrcRect
,
306 int nLeft
, int nTop
, int nRight
, int nBottom
,
307 HRGN
& rhInvalidateRgn
)
309 // calculate area outside the visible region
310 if (rSrcRect
.left
< nLeft
)
312 MakeInvisibleArea(rSrcRect
, -31999, 0, nLeft
, 31999, rhInvalidateRgn
);
314 if (rSrcRect
.top
< nTop
)
316 MakeInvisibleArea(rSrcRect
, 0, -31999, 31999, nTop
, rhInvalidateRgn
);
318 if (rSrcRect
.right
> nRight
)
320 MakeInvisibleArea(rSrcRect
, nRight
, 0, 31999, 31999, rhInvalidateRgn
);
322 if (rSrcRect
.bottom
> nBottom
)
324 MakeInvisibleArea(rSrcRect
, 0, nBottom
, 31999, 31999, rhInvalidateRgn
);
330 void WinSalGraphicsImpl::copyArea( long nDestX
, long nDestY
,
331 long nSrcX
, long nSrcY
,
332 long nSrcWidth
, long nSrcHeight
,
333 bool bWindowInvalidate
)
335 bool bRestoreClipRgn
= false;
336 HRGN hOldClipRgn
= nullptr;
337 int nOldClipRgnType
= ERROR
;
338 HRGN hInvalidateRgn
= nullptr;
340 // do we have to invalidate also the overlapping regions?
341 if ( bWindowInvalidate
&& mrParent
.isWindow() )
343 // compute and invalidate those parts that were either off-screen or covered by other windows
344 // while performing the above BitBlt
345 // those regions then have to be invalidated as they contain useless/wrong data
353 // restrict srcRect to this window (calc intersection)
354 aSrcRect
.left
= static_cast<int>(nSrcX
);
355 aSrcRect
.top
= static_cast<int>(nSrcY
);
356 aSrcRect
.right
= aSrcRect
.left
+static_cast<int>(nSrcWidth
);
357 aSrcRect
.bottom
= aSrcRect
.top
+static_cast<int>(nSrcHeight
);
358 GetClientRect( mrParent
.gethWnd(), &aClipRect
);
359 if ( IntersectRect( &aSrcRect
, &aSrcRect
, &aClipRect
) )
361 // transform srcRect to screen coordinates
365 ClientToScreen( mrParent
.gethWnd(), &aPt
);
366 aSrcRect
.left
+= aPt
.x
;
367 aSrcRect
.top
+= aPt
.y
;
368 aSrcRect
.right
+= aPt
.x
;
369 aSrcRect
.bottom
+= aPt
.y
;
370 hInvalidateRgn
= nullptr;
372 // compute the parts that are off screen (ie invisible)
374 ImplSalGetWorkArea( nullptr, &theScreen
, nullptr ); // find the screen area taking multiple monitors into account
375 ImplCalcOutSideRgn( aSrcRect
, theScreen
.left
, theScreen
.top
, theScreen
.right
, theScreen
.bottom
, hInvalidateRgn
);
377 // calculate regions that are covered by other windows
378 HRGN hTempRgn2
= nullptr;
379 HWND hWndTopWindow
= mrParent
.gethWnd();
380 // Find the TopLevel Window, because only Windows which are in
381 // in the foreground of our TopLevel window must be considered
382 if ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
)
384 RECT aTempRect3
= aSrcRect
;
387 hWndTopWindow
= ::GetParent( hWndTopWindow
);
389 // Test if the Parent clips our window
390 GetClientRect( hWndTopWindow
, &aTempRect
);
394 ClientToScreen( hWndTopWindow
, &aPt2
);
395 aTempRect
.left
+= aPt2
.x
;
396 aTempRect
.top
+= aPt2
.y
;
397 aTempRect
.right
+= aPt2
.x
;
398 aTempRect
.bottom
+= aPt2
.y
;
399 IntersectRect( &aTempRect3
, &aTempRect3
, &aTempRect
);
401 while ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
);
403 // If one or more Parents clip our window, then we must
404 // calculate the outside area
405 if ( !EqualRect( &aSrcRect
, &aTempRect3
) )
407 ImplCalcOutSideRgn( aSrcRect
,
408 aTempRect3
.left
, aTempRect3
.top
,
409 aTempRect3
.right
, aTempRect3
.bottom
,
413 // retrieve the top-most (z-order) child window
414 hWnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
417 if ( hWnd
== hWndTopWindow
)
419 if ( IsWindowVisible( hWnd
) && !IsIconic( hWnd
) )
421 GetWindowRect( hWnd
, &aTempRect
);
422 if ( IntersectRect( &aTempRect2
, &aSrcRect
, &aTempRect
) )
424 // hWnd covers part or all of aSrcRect
425 if ( !hInvalidateRgn
)
426 hInvalidateRgn
= CreateRectRgnIndirect( &aSrcRect
);
428 // get full bounding box of hWnd
429 hTempRgn
= CreateRectRgnIndirect( &aTempRect
);
431 // get region of hWnd (the window may be shaped)
433 hTempRgn2
= CreateRectRgn( 0, 0, 0, 0 );
434 int nRgnType
= GetWindowRgn( hWnd
, hTempRgn2
);
435 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
437 // convert window region to screen coordinates
438 OffsetRgn( hTempRgn2
, aTempRect
.left
, aTempRect
.top
);
439 // and intersect with the window's bounding box
440 CombineRgn( hTempRgn
, hTempRgn
, hTempRgn2
, RGN_AND
);
442 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
443 CombineRgn( hInvalidateRgn
, hInvalidateRgn
, hTempRgn
, RGN_DIFF
);
444 DeleteRegion( hTempRgn
);
447 // retrieve the next window in the z-order, i.e. the window below hwnd
448 hWnd
= GetWindow( hWnd
, GW_HWNDNEXT
);
451 DeleteRegion( hTempRgn2
);
452 if ( hInvalidateRgn
)
454 // hInvalidateRgn contains the fully visible parts of the original srcRect
455 hTempRgn
= CreateRectRgnIndirect( &aSrcRect
);
456 // subtract it from the original rect to get the occluded parts
457 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_DIFF
);
458 DeleteRegion( hTempRgn
);
460 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
462 // move the occluded parts to the destination pos
463 int nOffX
= static_cast<int>(nDestX
-nSrcX
);
464 int nOffY
= static_cast<int>(nDestY
-nSrcY
);
465 OffsetRgn( hInvalidateRgn
, nOffX
-aPt
.x
, nOffY
-aPt
.y
);
467 // by excluding hInvalidateRgn from the system's clip region
468 // we will prevent bitblt from copying useless data
469 // especially now shadows from overlapping windows will appear (#i36344)
470 hOldClipRgn
= CreateRectRgn( 0, 0, 0, 0 );
471 nOldClipRgnType
= GetClipRgn( mrParent
.getHDC(), hOldClipRgn
);
473 bRestoreClipRgn
= TRUE
; // indicate changed clipregion and force invalidate
474 ExtSelectClipRgn( mrParent
.getHDC(), hInvalidateRgn
, RGN_DIFF
);
480 BitBlt( mrParent
.getHDC(),
481 static_cast<int>(nDestX
), static_cast<int>(nDestY
),
482 static_cast<int>(nSrcWidth
), static_cast<int>(nSrcHeight
),
484 static_cast<int>(nSrcX
), static_cast<int>(nSrcY
),
487 if( bRestoreClipRgn
)
489 // restore old clip region
490 if( nOldClipRgnType
!= ERROR
)
491 SelectClipRgn( mrParent
.getHDC(), hOldClipRgn
);
492 DeleteRegion( hOldClipRgn
);
494 // invalidate regions that were not copied
495 bool bInvalidate
= true;
497 // Combine Invalidate vcl::Region with existing ClipRegion
498 HRGN hTempRgn
= CreateRectRgn( 0, 0, 0, 0 );
499 if ( GetClipRgn( mrParent
.getHDC(), hTempRgn
) == 1 )
501 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_AND
);
502 if ( (nRgnType
== ERROR
) || (nRgnType
== NULLREGION
) )
505 DeleteRegion( hTempRgn
);
509 InvalidateRgn( mrParent
.gethWnd(), hInvalidateRgn
, TRUE
);
510 // here we only initiate an update if this is the MainThread,
511 // so that there is no deadlock when handling the Paint event,
512 // as the SolarMutex is already held by this Thread
513 SalData
* pSalData
= GetSalData();
514 DWORD nCurThreadId
= GetCurrentThreadId();
515 if ( pSalData
->mnAppThreadId
== nCurThreadId
)
516 UpdateWindow( mrParent
.gethWnd() );
519 DeleteRegion( hInvalidateRgn
);
526 void ImplDrawBitmap( HDC hDC
, const SalTwoRect
& rPosAry
, const WinSalBitmap
& rSalBitmap
,
527 bool bPrinter
, int nDrawMode
)
532 HBITMAP hDrawDDB
= rSalBitmap
.ImplGethDDB();
533 std::unique_ptr
<WinSalBitmap
> xTmpSalBmp
;
534 bool bPrintDDB
= ( bPrinter
&& hDrawDDB
);
538 xTmpSalBmp
.reset(new WinSalBitmap
);
539 xTmpSalBmp
->Create( rSalBitmap
, rSalBitmap
.GetBitCount() );
540 hDrawDIB
= xTmpSalBmp
->ImplGethDIB();
543 hDrawDIB
= rSalBitmap
.ImplGethDIB();
547 PBITMAPINFO pBI
= static_cast<PBITMAPINFO
>(GlobalLock( hDrawDIB
));
548 PBYTE pBits
= reinterpret_cast<PBYTE
>(pBI
) + pBI
->bmiHeader
.biSize
+
549 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB
) * sizeof( RGBQUAD
);
550 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
553 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
554 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
555 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(pBI
->bmiHeader
.biHeight
- rPosAry
.mnSrcHeight
- rPosAry
.mnSrcY
),
556 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
557 pBits
, pBI
, DIB_RGB_COLORS
, nDrawMode
);
559 GlobalUnlock( hDrawDIB
);
560 SetStretchBltMode( hDC
, nOldStretchMode
);
562 else if( hDrawDDB
&& !bPrintDDB
)
564 ScopedCachedHDC
<CACHED_HDC_DRAW
> hBmpDC(hDrawDDB
);
566 COLORREF nOldBkColor
= RGB(0xFF,0xFF,0xFF);
567 COLORREF nOldTextColor
= RGB(0,0,0);
568 bool bMono
= ( rSalBitmap
.GetBitCount() == 1 );
572 COLORREF nBkColor
= RGB( 0xFF, 0xFF, 0xFF );
573 COLORREF nTextColor
= RGB( 0x00, 0x00, 0x00 );
574 //fdo#33455 handle 1 bit depth pngs with palette entries
575 //to set fore/back colors
576 if (BitmapBuffer
* pBitmapBuffer
= const_cast<WinSalBitmap
&>(rSalBitmap
).AcquireBuffer(BitmapAccessMode::Info
))
578 const BitmapPalette
& rPalette
= pBitmapBuffer
->maPalette
;
579 if (rPalette
.GetEntryCount() == 2)
581 Color nCol
= rPalette
[0];
582 nTextColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
584 nBkColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
586 const_cast<WinSalBitmap
&>(rSalBitmap
).ReleaseBuffer(pBitmapBuffer
, BitmapAccessMode::Info
);
588 nOldBkColor
= SetBkColor( hDC
, nBkColor
);
589 nOldTextColor
= ::SetTextColor( hDC
, nTextColor
);
592 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
593 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
596 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
597 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
599 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
604 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
607 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
608 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
610 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
611 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
614 SetStretchBltMode( hDC
, nOldStretchMode
);
619 SetBkColor( hDC
, nOldBkColor
);
620 ::SetTextColor( hDC
, nOldTextColor
);
628 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
630 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
634 // only paint direct when no scaling and no MapMode, else the
635 // more expensive conversions may be done for short-time Bitmap/BitmapEx
636 // used for buffering only
637 if(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&& rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
)
639 bTryDirectPaint
= false;
643 // try to draw using GdiPlus directly
644 if(bTryDirectPaint
&& TryDrawBitmapGDIPlus(rPosAry
, rSalBitmap
))
649 // fall back old stuff
650 assert(dynamic_cast<const WinSalBitmap
*>(&rSalBitmap
));
652 ImplDrawBitmap(mrParent
.getHDC(), rPosAry
, static_cast<const WinSalBitmap
&>(rSalBitmap
),
653 mrParent
.isPrinter(),
654 mbXORMode
? SRCINVERT
: SRCCOPY
);
657 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect
& rPosAry
,
658 const SalBitmap
& rSSalBitmap
,
659 const SalBitmap
& rSTransparentBitmap
)
661 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
662 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
664 // try to draw using GdiPlus directly
665 if(bTryDirectPaint
&& drawAlphaBitmap(rPosAry
, rSSalBitmap
, rSTransparentBitmap
))
670 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
671 assert(dynamic_cast<const WinSalBitmap
*>(&rSTransparentBitmap
));
673 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
674 const WinSalBitmap
& rTransparentBitmap
= static_cast<const WinSalBitmap
&>(rSTransparentBitmap
);
676 SalTwoRect aPosAry
= rPosAry
;
677 int nDstX
= static_cast<int>(aPosAry
.mnDestX
);
678 int nDstY
= static_cast<int>(aPosAry
.mnDestY
);
679 int nDstWidth
= static_cast<int>(aPosAry
.mnDestWidth
);
680 int nDstHeight
= static_cast<int>(aPosAry
.mnDestHeight
);
681 HDC hDC
= mrParent
.getHDC();
683 ScopedHBITMAP hMemBitmap
;
684 ScopedHBITMAP hMaskBitmap
;
686 if( ( nDstWidth
> CACHED_HDC_DEFEXT
) || ( nDstHeight
> CACHED_HDC_DEFEXT
) )
688 hMemBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
689 hMaskBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
692 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(hMemBitmap
.get());
693 ScopedCachedHDC
<CACHED_HDC_2
> hMaskDC(hMaskBitmap
.get());
695 aPosAry
.mnDestX
= aPosAry
.mnDestY
= 0;
696 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hDC
, nDstX
, nDstY
, SRCCOPY
);
698 // WIN/WNT seems to have a minor problem mapping the correct color of the
699 // mask to the palette if we draw the DIB directly ==> draw DDB
700 if( ( GetBitCount() <= 8 ) && rTransparentBitmap
.ImplGethDIB() && rTransparentBitmap
.GetBitCount() == 1 )
704 if( aTmp
.Create( rTransparentBitmap
, &mrParent
) )
705 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, aTmp
, false, SRCCOPY
);
708 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rTransparentBitmap
, false, SRCCOPY
);
710 // now MemDC contains background, MaskDC the transparency mask
712 // #105055# Respect XOR mode
715 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
716 // now MaskDC contains the bitmap area with black background
717 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCINVERT
);
718 // now MemDC contains background XORed bitmap area ontop
722 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCAND
);
723 // now MemDC contains background with masked-out bitmap area
724 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
725 // now MaskDC contains the bitmap area with black background
726 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCPAINT
);
727 // now MemDC contains background and bitmap merged together
730 BitBlt( hDC
, nDstX
, nDstY
, nDstWidth
, nDstHeight
, hMemDC
.get(), 0, 0, SRCCOPY
);
733 bool WinSalGraphicsImpl::drawAlphaRect( long nX
, long nY
, long nWidth
,
734 long nHeight
, sal_uInt8 nTransparency
)
736 if( mbPen
|| !mbBrush
|| mbXORMode
)
737 return false; // can only perform solid fills without XOR.
739 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(nullptr);
740 SetPixel( hMemDC
.get(), int(0), int(0), mnBrushColor
);
742 BLENDFUNCTION aFunc
= {
745 sal::static_int_cast
<sal_uInt8
>(255 - 255L*nTransparency
/100),
749 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
751 bool bRet
= GdiAlphaBlend(mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
,
752 hMemDC
.get(), 0,0,1,1,
758 void WinSalGraphicsImpl::drawMask(const SalTwoRect
& rPosAry
,
759 const SalBitmap
& rSSalBitmap
,
762 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
764 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
766 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
768 SalTwoRect aPosAry
= rPosAry
;
769 const HDC hDC
= mrParent
.getHDC();
771 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(RGB(nMaskColor
.GetRed(),
772 nMaskColor
.GetGreen(),
773 nMaskColor
.GetBlue())));
775 // WIN/WNT seems to have a minor problem mapping the correct color of the
776 // mask to the palette if we draw the DIB directly ==> draw DDB
777 if( ( GetBitCount() <= 8 ) && rSalBitmap
.ImplGethDIB() && rSalBitmap
.GetBitCount() == 1 )
781 if( aTmp
.Create( rSalBitmap
, &mrParent
) )
782 ImplDrawBitmap( hDC
, aPosAry
, aTmp
, false, 0x00B8074AUL
);
785 ImplDrawBitmap( hDC
, aPosAry
, rSalBitmap
, false, 0x00B8074AUL
);
788 std::shared_ptr
<SalBitmap
> WinSalGraphicsImpl::getBitmap( long nX
, long nY
, long nDX
, long nDY
)
790 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
792 std::shared_ptr
<WinSalBitmap
> pSalBitmap
;
797 HDC hDC
= mrParent
.getHDC();
798 HBITMAP hBmpBitmap
= CreateCompatibleBitmap( hDC
, nDX
, nDY
);
802 ScopedCachedHDC
<CACHED_HDC_1
> hBmpDC(hBmpBitmap
);
804 bRet
= BitBlt(hBmpDC
.get(), 0, 0,
805 static_cast<int>(nDX
), static_cast<int>(nDY
), hDC
,
806 static_cast<int>(nX
), static_cast<int>(nY
), SRCCOPY
) ? TRUE
: FALSE
;
811 pSalBitmap
= std::make_shared
<WinSalBitmap
>();
813 if( !pSalBitmap
->Create( hBmpBitmap
, FALSE
, FALSE
) )
820 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
821 DeleteBitmap( hBmpBitmap
);
827 Color
WinSalGraphicsImpl::getPixel( long nX
, long nY
)
829 COLORREF aWinCol
= ::GetPixel( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
) );
831 if ( CLR_INVALID
== aWinCol
)
832 return Color( 0, 0, 0 );
834 return Color( GetRValue( aWinCol
),
835 GetGValue( aWinCol
),
836 GetBValue( aWinCol
) );
842 HBRUSH
Get50PercentBrush()
844 SalData
* pSalData
= GetSalData();
845 if ( !pSalData
->mh50Brush
)
847 if ( !pSalData
->mh50Bmp
)
848 pSalData
->mh50Bmp
= ImplLoadSalBitmap( SAL_RESID_BITMAP_50
);
849 pSalData
->mh50Brush
= CreatePatternBrush( pSalData
->mh50Bmp
);
852 return pSalData
->mh50Brush
;
857 void WinSalGraphicsImpl::invert( long nX
, long nY
, long nWidth
, long nHeight
, SalInvert nFlags
)
859 if ( nFlags
& SalInvert::TrackFrame
)
861 HPEN hDotPen
= CreatePen( PS_DOT
, 0, 0 );
862 HPEN hOldPen
= SelectPen( mrParent
.getHDC(), hDotPen
);
863 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), GetStockBrush( NULL_BRUSH
) );
864 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
866 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
868 SetROP2( mrParent
.getHDC(), nOldROP
);
869 SelectPen( mrParent
.getHDC(), hOldPen
);
870 SelectBrush( mrParent
.getHDC(), hOldBrush
);
871 DeletePen( hDotPen
);
873 else if ( nFlags
& SalInvert::N50
)
875 COLORREF nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
876 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), Get50PercentBrush() );
877 PatBlt( mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
, PATINVERT
);
878 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
879 SelectBrush( mrParent
.getHDC(), hOldBrush
);
884 aRect
.left
= static_cast<int>(nX
);
885 aRect
.top
= static_cast<int>(nY
);
886 aRect
.right
= static_cast<int>(nX
)+nWidth
;
887 aRect
.bottom
= static_cast<int>(nY
)+nHeight
;
888 ::InvertRect( mrParent
.getHDC(), &aRect
);
892 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints
, const SalPoint
* pPtAry
, SalInvert nSalFlags
)
897 HBRUSH hOldBrush
= nullptr;
898 COLORREF nOldTextColor
RGB(0,0,0);
899 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
901 if ( nSalFlags
& SalInvert::TrackFrame
)
902 hPen
= CreatePen( PS_DOT
, 0, 0 );
906 if ( nSalFlags
& SalInvert::N50
)
907 hBrush
= Get50PercentBrush();
909 hBrush
= GetStockBrush( BLACK_BRUSH
);
911 hPen
= GetStockPen( NULL_PEN
);
912 nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
913 hOldBrush
= SelectBrush( mrParent
.getHDC(), hBrush
);
915 hOldPen
= SelectPen( mrParent
.getHDC(), hPen
);
917 POINT
const * pWinPtAry
;
918 // for NT, we can handover the array directly
919 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
921 pWinPtAry
= reinterpret_cast<POINT
const *>(pPtAry
);
922 // for Windows 95 and its maximum number of points
923 if ( nSalFlags
& SalInvert::TrackFrame
)
925 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
, static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
926 Polyline( mrParent
.getHDC(), pWinPtAry
, MAX_64KSALPOINTS
);
930 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
, static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
931 Polygon( mrParent
.getHDC(), pWinPtAry
, MAX_64KSALPOINTS
);
934 SetROP2( mrParent
.getHDC(), nOldROP
);
935 SelectPen( mrParent
.getHDC(), hOldPen
);
937 if ( nSalFlags
& SalInvert::TrackFrame
)
941 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
942 SelectBrush( mrParent
.getHDC(), hOldBrush
);
946 sal_uInt16
WinSalGraphicsImpl::GetBitCount() const
948 return static_cast<sal_uInt16
>(GetDeviceCaps( mrParent
.getHDC(), BITSPIXEL
));
951 long WinSalGraphicsImpl::GetGraphicsWidth() const
953 if( mrParent
.gethWnd() && IsWindow( mrParent
.gethWnd() ) )
955 WinSalFrame
* pFrame
= GetWindowPtr( mrParent
.gethWnd() );
958 if( pFrame
->maGeometry
.nWidth
)
959 return pFrame
->maGeometry
.nWidth
;
962 // TODO: perhaps not needed, maGeometry should always be up-to-date
964 GetClientRect( mrParent
.gethWnd(), &aRect
);
973 void WinSalGraphicsImpl::ResetClipRegion()
975 if ( mrParent
.mhRegion
)
977 DeleteRegion( mrParent
.mhRegion
);
978 mrParent
.mhRegion
= nullptr;
981 SelectClipRgn( mrParent
.getHDC(), nullptr );
984 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon
& rCandidate
)
986 if(rCandidate
.areControlPointsUsed())
991 const sal_uInt32
nPointCount(rCandidate
.count());
998 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
+ 1 : nPointCount
);
999 basegfx::B2DPoint
aLast(rCandidate
.getB2DPoint(0));
1001 for(sal_uInt32
a(1); a
< nEdgeCount
; a
++)
1003 const sal_uInt32
nNextIndex(a
% nPointCount
);
1004 const basegfx::B2DPoint
aCurrent(rCandidate
.getB2DPoint(nNextIndex
));
1006 if(!basegfx::fTools::equal(aLast
.getX(), aCurrent
.getX()) && !basegfx::fTools::equal(aLast
.getY(), aCurrent
.getY()))
1017 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon
& rCandidate
)
1019 if(rCandidate
.areControlPointsUsed())
1024 for(auto const& rPolygon
: rCandidate
)
1026 if(!containsOnlyHorizontalAndVerticalEdges(rPolygon
))
1035 bool WinSalGraphicsImpl::setClipRegion( const vcl::Region
& i_rClip
)
1037 if ( mrParent
.mhRegion
)
1039 DeleteRegion( mrParent
.mhRegion
);
1040 mrParent
.mhRegion
= nullptr;
1043 bool bUsePolygon(i_rClip
.HasPolyPolygonOrB2DPolyPolygon());
1044 static bool bTryToAvoidPolygon(true);
1046 // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1047 // and only contains horizontal/vertical edges. In that case, use the fallback
1048 // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1049 // the correct polygon-to-RegionBand transformation.
1050 // Background is that when using the same Rectangle as rectangle or as Polygon
1051 // clip region will lead to different results; the polygon-based one will be
1052 // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1053 // again is because of the polygon-nature and it's classic handling when filling.
1054 // This also means that all cases which use a 'true' polygon-based incarnation of
1055 // a vcl::Region should know what they do - it may lead to repaint errors.
1056 if(bUsePolygon
&& bTryToAvoidPolygon
)
1058 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1060 if(!aPolyPolygon
.areControlPointsUsed())
1062 if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon
))
1064 bUsePolygon
= false;
1071 // #i122149# check the comment above to know that this may lead to potential repaint
1072 // problems. It may be solved (if needed) by scaling the polygon by one in X
1073 // and Y. Currently the workaround to only use it if really unavoidable will
1074 // solve most cases. When someone is really using polygon-based Regions he
1075 // should know what he is doing.
1076 // Added code to do that scaling to check if it works, testing it.
1077 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1078 const sal_uInt32
nCount(aPolyPolygon
.count());
1082 std::vector
< POINT
> aPolyPoints
;
1083 aPolyPoints
.reserve( 1024 );
1084 std::vector
< INT
> aPolyCounts( nCount
, 0 );
1085 basegfx::B2DHomMatrix aExpand
;
1086 sal_uInt32
nTargetCount(0);
1087 static bool bExpandByOneInXandY(true);
1089 if(bExpandByOneInXandY
)
1091 const basegfx::B2DRange
aRangeS(aPolyPolygon
.getB2DRange());
1092 const basegfx::B2DRange
aRangeT(aRangeS
.getMinimum(), aRangeS
.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1093 aExpand
= basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS
, aRangeT
);
1096 for(auto const& rPolygon
: aPolyPolygon
)
1098 const basegfx::B2DPolygon
aPoly(
1099 basegfx::utils::adaptiveSubdivideByDistance(
1102 const sal_uInt32
nPoints(aPoly
.count());
1104 // tdf#40863 For CustomShapes there is a hack (see
1105 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1106 // with a single point in top-left and bottom-right corner
1107 // of the BoundRect to be able to determine the correct BoundRect
1108 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1109 // fails with polygons containing a single pixel, so clipping is
1110 // lost. For now, use only polygons with more than two points - the
1111 // ones that may have an area.
1112 // Note: polygons with one point which are curves may have an area,
1113 // but the polygon is already subdivided here, so no need to test
1117 aPolyCounts
[nTargetCount
] = nPoints
;
1120 for( sal_uInt32 b
= 0; b
< nPoints
; b
++ )
1122 basegfx::B2DPoint
aPt(aPoly
.getB2DPoint(b
));
1124 if(bExpandByOneInXandY
)
1126 aPt
= aExpand
* aPt
;
1130 // #i122149# do correct rounding
1131 aPOINT
.x
= basegfx::fround(aPt
.getX());
1132 aPOINT
.y
= basegfx::fround(aPt
.getY());
1133 aPolyPoints
.push_back( aPOINT
);
1140 mrParent
.mhRegion
= CreatePolyPolygonRgn( aPolyPoints
.data(), aPolyCounts
.data(), nTargetCount
, ALTERNATE
);
1146 RectangleVector aRectangles
;
1147 i_rClip
.GetRegionRectangles(aRectangles
);
1149 sal_uLong nRectBufSize
= sizeof(RECT
)*aRectangles
.size();
1150 if ( aRectangles
.size() < SAL_CLIPRECT_COUNT
)
1152 if ( !mrParent
.mpStdClipRgnData
)
1153 mrParent
.mpStdClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+(SAL_CLIPRECT_COUNT
*sizeof(RECT
))]);
1154 mrParent
.mpClipRgnData
= mrParent
.mpStdClipRgnData
;
1157 mrParent
.mpClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+nRectBufSize
]);
1158 mrParent
.mpClipRgnData
->rdh
.dwSize
= sizeof( RGNDATAHEADER
);
1159 mrParent
.mpClipRgnData
->rdh
.iType
= RDH_RECTANGLES
;
1160 mrParent
.mpClipRgnData
->rdh
.nCount
= aRectangles
.size();
1161 mrParent
.mpClipRgnData
->rdh
.nRgnSize
= nRectBufSize
;
1162 RECT
* pBoundRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1163 SetRectEmpty( pBoundRect
);
1164 RECT
* pNextClipRect
= reinterpret_cast<RECT
*>(&(mrParent
.mpClipRgnData
->Buffer
));
1165 bool bFirstClipRect
= true;
1167 for (auto const& rectangle
: aRectangles
)
1169 const long nW(rectangle
.GetWidth());
1170 const long nH(rectangle
.GetHeight());
1174 const long nRight(rectangle
.Left() + nW
);
1175 const long nBottom(rectangle
.Top() + nH
);
1179 pBoundRect
->left
= rectangle
.Left();
1180 pBoundRect
->top
= rectangle
.Top();
1181 pBoundRect
->right
= nRight
;
1182 pBoundRect
->bottom
= nBottom
;
1183 bFirstClipRect
= false;
1187 if(rectangle
.Left() < pBoundRect
->left
)
1189 pBoundRect
->left
= static_cast<int>(rectangle
.Left());
1192 if(rectangle
.Top() < pBoundRect
->top
)
1194 pBoundRect
->top
= static_cast<int>(rectangle
.Top());
1197 if(nRight
> pBoundRect
->right
)
1199 pBoundRect
->right
= static_cast<int>(nRight
);
1202 if(nBottom
> pBoundRect
->bottom
)
1204 pBoundRect
->bottom
= static_cast<int>(nBottom
);
1208 pNextClipRect
->left
= static_cast<int>(rectangle
.Left());
1209 pNextClipRect
->top
= static_cast<int>(rectangle
.Top());
1210 pNextClipRect
->right
= static_cast<int>(nRight
);
1211 pNextClipRect
->bottom
= static_cast<int>(nBottom
);
1216 mrParent
.mpClipRgnData
->rdh
.nCount
--;
1217 mrParent
.mpClipRgnData
->rdh
.nRgnSize
-= sizeof( RECT
);
1221 // create clip region from ClipRgnData
1222 if(0 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1224 // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1225 // that contains no polygons or only empty ones (no width/height). This is
1226 // perfectly fine and we are done, except setting it (see end of method)
1228 else if(1 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1230 RECT
* pRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1231 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
,
1232 pRect
->right
, pRect
->bottom
);
1234 else if(mrParent
.mpClipRgnData
->rdh
.nCount
> 1)
1236 sal_uLong nSize
= mrParent
.mpClipRgnData
->rdh
.nRgnSize
+sizeof(RGNDATAHEADER
);
1237 mrParent
.mhRegion
= ExtCreateRegion( nullptr, nSize
, mrParent
.mpClipRgnData
);
1239 // if ExtCreateRegion(...) is not supported
1240 if( !mrParent
.mhRegion
)
1242 RGNDATAHEADER
const & pHeader
= mrParent
.mpClipRgnData
->rdh
;
1244 if( pHeader
.nCount
)
1246 RECT
* pRect
= reinterpret_cast<RECT
*>(mrParent
.mpClipRgnData
->Buffer
);
1247 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
);
1250 for( sal_uLong n
= 1; n
< pHeader
.nCount
; n
++, pRect
++ )
1252 ScopedHRGN
hRgn(CreateRectRgn(pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
));
1253 CombineRgn( mrParent
.mhRegion
, mrParent
.mhRegion
, hRgn
.get(), RGN_OR
);
1258 if ( mrParent
.mpClipRgnData
!= mrParent
.mpStdClipRgnData
)
1259 delete [] reinterpret_cast<BYTE
*>(mrParent
.mpClipRgnData
);
1263 if( mrParent
.mhRegion
)
1265 SelectClipRgn( mrParent
.getHDC(), mrParent
.mhRegion
);
1267 // debug code if you want to check range of the newly applied ClipRegion
1269 //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1275 // #i123585# See above, this is a valid case, execute it
1276 SelectClipRgn( mrParent
.getHDC(), nullptr );
1279 // #i123585# retval no longer dependent of mrParent.mhRegion, see TaskId comments above
1283 void WinSalGraphicsImpl::SetLineColor()
1285 ResetPen(GetStockPen(NULL_PEN
));
1292 void WinSalGraphicsImpl::SetLineColor(Color nColor
)
1294 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1297 bool bStockPen
= false;
1299 HPEN hNewPen
= SearchStockPen(nPenColor
);
1303 hNewPen
= MakePen(nColor
);
1308 mnPenColor
= nPenColor
;
1309 maLineColor
= nColor
;
1311 mbStockPen
= bStockPen
;
1314 HPEN
WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor
)
1316 // Only screen, because printer has problems, when we use stock objects.
1317 if (!mrParent
.isPrinter())
1319 const SalData
* pSalData
= GetSalData();
1321 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockPenCount
; i
++)
1323 if (nPenColor
== pSalData
->maStockPenColorAry
[i
])
1324 return pSalData
->mhStockPenAry
[i
];
1331 HPEN
WinSalGraphicsImpl::MakePen(Color nColor
)
1333 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1337 if (!mrParent
.isPrinter())
1339 if (GetSalData()->mhDitherPal
&& ImplIsSysColorEntry(nColor
))
1341 nPenColor
= PALRGB_TO_RGB(nPenColor
);
1345 return CreatePen(PS_SOLID
, mrParent
.mnPenWidth
, nPenColor
);
1348 void WinSalGraphicsImpl::ResetPen(HPEN hNewPen
)
1350 HPEN hOldPen
= SelectPen(mrParent
.getHDC(), hNewPen
);
1361 mrParent
.mhDefPen
= hOldPen
;
1367 void WinSalGraphicsImpl::SetFillColor()
1369 ResetBrush(GetStockBrush(NULL_BRUSH
));
1373 mbStockBrush
= TRUE
;
1376 void WinSalGraphicsImpl::SetFillColor(Color nColor
)
1378 COLORREF nBrushColor
= PALETTERGB(nColor
.GetRed(),
1381 bool bStockBrush
= false;
1383 HBRUSH hNewBrush
= SearchStockBrush(nBrushColor
);
1387 hNewBrush
= MakeBrush(nColor
);
1389 ResetBrush(hNewBrush
);
1392 mnBrushColor
= nBrushColor
;
1393 maFillColor
= nColor
;
1395 mbStockBrush
= bStockBrush
;
1398 HBRUSH
WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor
)
1400 // Only screen, because printer has problems, when we use stock objects.
1401 if (!mrParent
.isPrinter())
1403 const SalData
* pSalData
= GetSalData();
1405 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockBrushCount
; i
++)
1407 if (nBrushColor
== pSalData
->maStockBrushColorAry
[i
])
1408 return pSalData
->mhStockBrushAry
[i
];
1418 BYTE
GetDitherMappingValue(BYTE nVal
, BYTE nThres
, const SalData
* pSalData
)
1420 return (pSalData
->mpDitherDiff
[nVal
] > nThres
) ?
1421 pSalData
->mpDitherHigh
[nVal
] : pSalData
->mpDitherLow
[nVal
];
1424 HBRUSH
Make16BitDIBPatternBrush(Color nColor
)
1426 const SalData
* pSalData
= GetSalData();
1428 const BYTE nRed
= nColor
.GetRed();
1429 const BYTE nGreen
= nColor
.GetGreen();
1430 const BYTE nBlue
= nColor
.GetBlue();
1432 static const BYTE aOrdDither16Bit
[8][8] =
1434 { 0, 6, 1, 7, 0, 6, 1, 7 },
1435 { 4, 2, 5, 3, 4, 2, 5, 3 },
1436 { 1, 7, 0, 6, 1, 7, 0, 6 },
1437 { 5, 3, 4, 2, 5, 3, 4, 2 },
1438 { 0, 6, 1, 7, 0, 6, 1, 7 },
1439 { 4, 2, 5, 3, 4, 2, 5, 3 },
1440 { 1, 7, 0, 6, 1, 7, 0, 6 },
1441 { 5, 3, 4, 2, 5, 3, 4, 2 }
1444 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1446 for(int nY
= 0; nY
< 8; ++nY
)
1448 for(int nX
= 0; nX
< 8; ++nX
)
1450 const BYTE nThres
= aOrdDither16Bit
[nY
][nX
];
1451 *pTmp
++ = GetDitherMappingValue(nBlue
, nThres
, pSalData
);
1452 *pTmp
++ = GetDitherMappingValue(nGreen
, nThres
, pSalData
);
1453 *pTmp
++ = GetDitherMappingValue(nRed
, nThres
, pSalData
);
1457 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_RGB_COLORS
);
1460 HBRUSH
Make8BitDIBPatternBrush(Color nColor
)
1462 const SalData
* pSalData
= GetSalData();
1464 const BYTE nRed
= nColor
.GetRed();
1465 const BYTE nGreen
= nColor
.GetGreen();
1466 const BYTE nBlue
= nColor
.GetBlue();
1468 static const BYTE aOrdDither8Bit
[8][8] =
1470 { 0, 38, 9, 48, 2, 40, 12, 50 },
1471 { 25, 12, 35, 22, 28, 15, 37, 24 },
1472 { 6, 44, 3, 41, 8, 47, 5, 44 },
1473 { 32, 19, 28, 16, 34, 21, 31, 18 },
1474 { 1, 40, 11, 49, 0, 39, 10, 48 },
1475 { 27, 14, 36, 24, 26, 13, 36, 23 },
1476 { 8, 46, 4, 43, 7, 45, 4, 42 },
1477 { 33, 20, 30, 17, 32, 20, 29, 16 }
1480 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1482 for (int nY
= 0; nY
< 8; ++nY
)
1484 for (int nX
= 0; nX
< 8; ++nX
)
1486 const BYTE nThres
= aOrdDither8Bit
[nY
][nX
];
1487 *pTmp
= GetDitherMappingValue(nRed
, nThres
, pSalData
) +
1488 GetDitherMappingValue(nGreen
, nThres
, pSalData
) * 6 +
1489 GetDitherMappingValue(nBlue
, nThres
, pSalData
) * 36;
1494 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_PAL_COLORS
);
1499 HBRUSH
WinSalGraphicsImpl::MakeBrush(Color nColor
)
1501 const SalData
* pSalData
= GetSalData();
1503 const BYTE nRed
= nColor
.GetRed();
1504 const BYTE nGreen
= nColor
.GetGreen();
1505 const BYTE nBlue
= nColor
.GetBlue();
1506 const COLORREF nBrushColor
= PALETTERGB(nRed
, nGreen
, nBlue
);
1508 if (mrParent
.isPrinter() || !pSalData
->mhDitherDIB
)
1509 return CreateSolidBrush(nBrushColor
);
1511 if (24 == reinterpret_cast<BITMAPINFOHEADER
*>(pSalData
->mpDitherDIB
)->biBitCount
)
1512 return Make16BitDIBPatternBrush(nColor
);
1514 if (ImplIsSysColorEntry(nColor
))
1515 return CreateSolidBrush(PALRGB_TO_RGB(nBrushColor
));
1517 if (ImplIsPaletteEntry(nRed
, nGreen
, nBlue
))
1518 return CreateSolidBrush(nBrushColor
);
1520 return Make8BitDIBPatternBrush(nColor
);
1523 void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush
)
1525 HBRUSH hOldBrush
= SelectBrush(mrParent
.getHDC(), hNewBrush
);
1531 DeleteBrush(mhBrush
);
1536 mrParent
.mhDefBrush
= hOldBrush
;
1539 mhBrush
= hNewBrush
;
1542 void WinSalGraphicsImpl::SetXORMode( bool bSet
, bool )
1545 ::SetROP2( mrParent
.getHDC(), bSet
? R2_XORPEN
: R2_COPYPEN
);
1548 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor
)
1550 SetLineColor( ImplGetROPColor( nROPColor
) );
1553 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor
)
1555 SetFillColor( ImplGetROPColor( nROPColor
) );
1558 void WinSalGraphicsImpl::DrawPixelImpl( long nX
, long nY
, COLORREF crColor
)
1560 const HDC hDC
= mrParent
.getHDC();
1564 SetPixel(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), crColor
);
1568 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(crColor
));
1569 PatBlt(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), int(1), int(1), PATINVERT
);
1572 void WinSalGraphicsImpl::drawPixel( long nX
, long nY
)
1574 DrawPixelImpl( nX
, nY
, mnPenColor
);
1577 void WinSalGraphicsImpl::drawPixel( long nX
, long nY
, Color nColor
)
1579 COLORREF nCol
= PALETTERGB( nColor
.GetRed(),
1583 if ( !mrParent
.isPrinter() &&
1584 GetSalData()->mhDitherPal
&&
1585 ImplIsSysColorEntry( nColor
) )
1586 nCol
= PALRGB_TO_RGB( nCol
);
1588 DrawPixelImpl( nX
, nY
, nCol
);
1591 void WinSalGraphicsImpl::drawLine( long nX1
, long nY1
, long nX2
, long nY2
)
1593 MoveToEx( mrParent
.getHDC(), static_cast<int>(nX1
), static_cast<int>(nY1
), nullptr );
1595 LineTo( mrParent
.getHDC(), static_cast<int>(nX2
), static_cast<int>(nY2
) );
1597 // LineTo doesn't draw the last pixel
1598 if ( !mrParent
.isPrinter() )
1599 DrawPixelImpl( nX2
, nY2
, mnPenColor
);
1602 void WinSalGraphicsImpl::drawRect( long nX
, long nY
, long nWidth
, long nHeight
)
1606 if ( !mrParent
.isPrinter() )
1608 PatBlt( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nWidth
), static_cast<int>(nHeight
),
1609 mbXORMode
? PATINVERT
: PATCOPY
);
1616 aWinRect
.right
= nX
+nWidth
;
1617 aWinRect
.bottom
= nY
+nHeight
;
1618 ::FillRect( mrParent
.getHDC(), &aWinRect
, mhBrush
);
1622 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
1625 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
1627 // for NT, we can handover the array directly
1628 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1630 POINT
const * pWinPtAry
= reinterpret_cast<POINT
const *>(pPtAry
);
1632 // for Windows 95 and its maximum number of points
1633 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
, static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1634 Polyline( mrParent
.getHDC(), pWinPtAry
, MAX_64KSALPOINTS
);
1636 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1637 if ( !mrParent
.isPrinter() )
1638 DrawPixelImpl( pWinPtAry
[nPoints
-1].x
, pWinPtAry
[nPoints
-1].y
, mnPenColor
);
1641 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints
, const SalPoint
* pPtAry
)
1643 // for NT, we can handover the array directly
1644 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1646 POINT
const * pWinPtAry
= reinterpret_cast<POINT
const *>(pPtAry
);
1647 // for Windows 95 and its maximum number of points
1648 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
, static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1649 Polygon( mrParent
.getHDC(), pWinPtAry
, MAX_64KSALPOINTS
);
1652 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1653 PCONSTSALPOINT
* pPtAry
)
1655 UINT aWinPointAry
[SAL_POLYPOLYCOUNT_STACKBUF
];
1657 UINT nPolyPolyPoints
= 0;
1661 if ( nPoly
<= SAL_POLYPOLYCOUNT_STACKBUF
)
1662 pWinPointAry
= aWinPointAry
;
1664 pWinPointAry
= new UINT
[nPoly
];
1666 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1668 nPoints
= static_cast<UINT
>(pPoints
[i
])+1;
1669 pWinPointAry
[i
] = nPoints
;
1670 nPolyPolyPoints
+= nPoints
;
1673 POINT aWinPointAryAry
[SAL_POLYPOLYPOINTS_STACKBUF
];
1674 POINT
* pWinPointAryAry
;
1675 if ( nPolyPolyPoints
<= SAL_POLYPOLYPOINTS_STACKBUF
)
1676 pWinPointAryAry
= aWinPointAryAry
;
1678 pWinPointAryAry
= new POINT
[nPolyPolyPoints
];
1679 // for NT, we can handover the array directly
1680 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1682 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1684 nPoints
= pWinPointAry
[i
];
1685 const SalPoint
* pPolyAry
= pPtAry
[i
];
1686 memcpy( pWinPointAryAry
+n
, pPolyAry
, (nPoints
-1)*sizeof(POINT
) );
1687 pWinPointAryAry
[n
+nPoints
-1] = pWinPointAryAry
[n
];
1691 if ( !PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), static_cast<UINT
>(nPoly
) ) &&
1692 (nPolyPolyPoints
> MAX_64KSALPOINTS
) )
1694 nPolyPolyPoints
= 0;
1698 nPolyPolyPoints
+= pWinPointAry
[static_cast<UINT
>(nPoly
)];
1701 while ( nPolyPolyPoints
< MAX_64KSALPOINTS
);
1703 if ( pWinPointAry
[static_cast<UINT
>(nPoly
)] > MAX_64KSALPOINTS
)
1704 pWinPointAry
[static_cast<UINT
>(nPoly
)] = MAX_64KSALPOINTS
;
1706 Polygon( mrParent
.getHDC(), pWinPointAryAry
, *pWinPointAry
);
1708 PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), nPoly
);
1711 if ( pWinPointAry
!= aWinPointAry
)
1712 delete [] pWinPointAry
;
1713 if ( pWinPointAryAry
!= aWinPointAryAry
)
1714 delete [] pWinPointAryAry
;
1717 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints
, const SalPoint
* pPtAry
, const PolyFlags
* pFlgAry
)
1719 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1721 // #100127# draw an array of points which might also contain bezier control points
1725 const HDC hdc
= mrParent
.getHDC();
1727 // TODO: profile whether the following options are faster:
1728 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1729 // b) convert our flag array to window's and use PolyDraw
1730 MoveToEx(hdc
, pPtAry
->mnX
, pPtAry
->mnY
, nullptr);
1734 for(sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1736 if(*pFlgAry
!= PolyFlags::Control
)
1738 LineTo(hdc
, pPtAry
->mnX
, pPtAry
->mnY
);
1740 else if(nPoints
- i
> 2)
1742 PolyBezierTo(hdc
, reinterpret_cast<const POINT
*>(pPtAry
), 3);
1755 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints
, const SalPoint
* pPtAry
, const PolyFlags
* pFlgAry
)
1757 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1759 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1760 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1761 POINT
* pWinPointAry
;
1763 if( nPoints
> SAL_POLY_STACKBUF
)
1765 pWinPointAry
= new POINT
[ nPoints
];
1766 pWinFlagAry
= new BYTE
[ nPoints
];
1770 pWinPointAry
= aStackAry1
;
1771 pWinFlagAry
= aStackAry2
;
1774 sal_uInt32
nPoints_i32(nPoints
);
1775 ImplPreparePolyDraw(true, 1, &nPoints_i32
, &pPtAry
, &pFlgAry
, pWinPointAry
, pWinFlagAry
);
1779 if( BeginPath( mrParent
.getHDC() ) )
1781 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nPoints
);
1783 if( EndPath( mrParent
.getHDC() ) )
1785 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1790 if( pWinPointAry
!= aStackAry1
)
1792 delete [] pWinPointAry
;
1793 delete [] pWinFlagAry
;
1799 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1800 const SalPoint
* const* pPtAry
, const PolyFlags
* const* pFlgAry
)
1802 static_assert( sizeof( POINT
) == sizeof( SalPoint
), "must be the same size" );
1804 sal_uLong nCurrPoly
, nTotalPoints
;
1805 const sal_uInt32
* pCurrPoints
= pPoints
;
1806 for( nCurrPoly
=0, nTotalPoints
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
1807 nTotalPoints
+= *pCurrPoints
++;
1809 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1810 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1811 POINT
* pWinPointAry
;
1813 if( nTotalPoints
> SAL_POLY_STACKBUF
)
1815 pWinPointAry
= new POINT
[ nTotalPoints
];
1816 pWinFlagAry
= new BYTE
[ nTotalPoints
];
1820 pWinPointAry
= aStackAry1
;
1821 pWinFlagAry
= aStackAry2
;
1824 ImplPreparePolyDraw(true, nPoly
, pPoints
, pPtAry
, pFlgAry
, pWinPointAry
, pWinFlagAry
);
1828 if( BeginPath( mrParent
.getHDC() ) )
1830 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nTotalPoints
);
1832 if( EndPath( mrParent
.getHDC() ) )
1834 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1839 if( pWinPointAry
!= aStackAry1
)
1841 delete [] pWinPointAry
;
1842 delete [] pWinFlagAry
;
1848 static basegfx::B2DPoint
impPixelSnap(
1849 const basegfx::B2DPolygon
& rPolygon
,
1850 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1851 basegfx::B2DHomMatrix
& rObjectToDeviceInv
,
1854 const sal_uInt32
nCount(rPolygon
.count());
1857 const basegfx::B2ITuple
aPrevTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ nCount
- 1) % nCount
)));
1858 const basegfx::B2DPoint
aCurrPoint(rObjectToDevice
* rPolygon
.getB2DPoint(nIndex
));
1859 const basegfx::B2ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1860 const basegfx::B2ITuple
aNextTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ 1) % nCount
)));
1863 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1864 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1865 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1866 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1867 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1868 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1870 if(bSnapX
|| bSnapY
)
1872 basegfx::B2DPoint
aSnappedPoint(
1873 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1874 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY());
1876 if(rObjectToDeviceInv
.isIdentity())
1878 rObjectToDeviceInv
= rObjectToDevice
;
1879 rObjectToDeviceInv
.invert();
1882 aSnappedPoint
*= rObjectToDeviceInv
;
1884 return aSnappedPoint
;
1887 return rPolygon
.getB2DPoint(nIndex
);
1890 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1891 Gdiplus::GraphicsPath
& rGraphicsPath
,
1892 const basegfx::B2DPolygon
& rPolygon
,
1893 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1895 bool bPixelSnapHairline
)
1897 sal_uInt32
nCount(rPolygon
.count());
1901 const sal_uInt32
nEdgeCount(rPolygon
.isClosed() ? nCount
: nCount
- 1);
1905 const bool bControls(rPolygon
.areControlPointsUsed());
1906 basegfx::B2DPoint
aCurr(rPolygon
.getB2DPoint(0));
1907 basegfx::B2DHomMatrix aObjectToDeviceInv
;
1909 if(bPixelSnapHairline
)
1911 aCurr
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, 0);
1914 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1916 const sal_uInt32
nNextIndex((a
+ 1) % nCount
);
1917 basegfx::B2DPoint
aNext(rPolygon
.getB2DPoint(nNextIndex
));
1918 const bool b1stControlPointUsed(bControls
&& rPolygon
.isNextControlPointUsed(a
));
1919 const bool b2ndControlPointUsed(bControls
&& rPolygon
.isPrevControlPointUsed(nNextIndex
));
1921 if(bPixelSnapHairline
)
1923 aNext
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, nNextIndex
);
1926 if(b1stControlPointUsed
|| b2ndControlPointUsed
)
1928 basegfx::B2DPoint
aCa(rPolygon
.getNextControlPoint(a
));
1929 basegfx::B2DPoint
aCb(rPolygon
.getPrevControlPoint(nNextIndex
));
1931 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1932 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1933 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1934 // (basegfx can handle that).
1935 // Caution: This error (and it's correction) might be necessary for other graphical
1936 // sub-systems in a similar way.
1937 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1938 // vector was wrong. Best alternative is one as close as possible which means short.
1939 if(!b1stControlPointUsed
)
1941 aCa
= aCurr
+ ((aCb
- aCurr
) * 0.0005);
1943 else if(!b2ndControlPointUsed
)
1945 aCb
= aNext
+ ((aCa
- aNext
) * 0.0005);
1948 rGraphicsPath
.AddBezier(
1949 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1950 static_cast< Gdiplus::REAL
>(aCa
.getX()), static_cast< Gdiplus::REAL
>(aCa
.getY()),
1951 static_cast< Gdiplus::REAL
>(aCb
.getX()), static_cast< Gdiplus::REAL
>(aCb
.getY()),
1952 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1956 rGraphicsPath
.AddLine(
1957 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1958 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1961 if(a
+ 1 < nEdgeCount
)
1967 rGraphicsPath
.StartFigure();
1975 class SystemDependentData_GraphicsPath
: public basegfx::SystemDependentData
1978 // the path data itself
1979 std::shared_ptr
<Gdiplus::GraphicsPath
> mpGraphicsPath
;
1981 // all other values the triangulation is based on and
1982 // need to be compared with to check for data validity
1986 SystemDependentData_GraphicsPath(
1987 basegfx::SystemDependentDataManager
& rSystemDependentDataManager
,
1988 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
1991 // read access to Gdiplus::GraphicsPath
1992 std::shared_ptr
<Gdiplus::GraphicsPath
>& getGraphicsPath() { return mpGraphicsPath
; }
1994 // other data-validity access
1995 bool getNoLineJoin() const { return mbNoLineJoin
; }
1997 virtual sal_Int64
estimateUsageInBytes() const override
;
2000 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
2001 basegfx::SystemDependentDataManager
& rSystemDependentDataManager
,
2002 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
2004 : basegfx::SystemDependentData(rSystemDependentDataManager
),
2005 mpGraphicsPath(rpGraphicsPath
),
2006 mbNoLineJoin(bNoLineJoin
)
2010 sal_Int64
SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2012 sal_Int64
nRetval(0);
2016 const INT
nPointCount(mpGraphicsPath
->GetPointCount());
2018 if(0 != nPointCount
)
2021 // - 2 x sizeof(Gdiplus::REAL)
2022 // - 1 byte (see GetPathTypes in docu)
2023 nRetval
= nPointCount
* ((2 * sizeof(Gdiplus::REAL
)) + 1);
2030 bool WinSalGraphicsImpl::drawPolyPolygon(
2031 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2032 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
2033 double fTransparency
)
2035 const sal_uInt32
nCount(rPolyPolygon
.count());
2037 if(!mbBrush
|| 0 == nCount
|| fTransparency
< 0.0 || fTransparency
> 1.0)
2042 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2043 const sal_uInt8
aTrans(sal_uInt8(255) - static_cast<sal_uInt8
>(basegfx::fround(fTransparency
* 255.0)));
2044 const Gdiplus::Color
aTestColor(aTrans
, maFillColor
.GetRed(), maFillColor
.GetGreen(), maFillColor
.GetBlue());
2045 const Gdiplus::SolidBrush
aSolidBrush(aTestColor
.GetValue());
2047 // Set full (Object-to-Device) transformation - if used
2048 if(rObjectToDevice
.isIdentity())
2050 aGraphics
.ResetTransform();
2054 Gdiplus::Matrix aMatrix
;
2056 aMatrix
.SetElements(
2057 rObjectToDevice
.get(0, 0),
2058 rObjectToDevice
.get(1, 0),
2059 rObjectToDevice
.get(0, 1),
2060 rObjectToDevice
.get(1, 1),
2061 rObjectToDevice
.get(0, 2),
2062 rObjectToDevice
.get(1, 2));
2063 aGraphics
.SetTransform(&aMatrix
);
2066 // prepare local instance of Gdiplus::GraphicsPath
2067 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2069 // try to access buffered data
2070 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2071 rPolyPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2073 if(pSystemDependentData_GraphicsPath
)
2075 // copy buffered data
2076 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2080 // Note: In principle we could use the same buffered geometry at line
2081 // and fill polygons. Checked that in a first try, used
2082 // GraphicsPath::AddPath from Gdiplus combined with below used
2083 // StartFigure/CloseFigure, worked well (thus the line-draw version
2084 // may create non-closed partial Polygon data).
2086 // But in current reality it gets not used due to e.g.
2087 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2088 // line and fill polygon-primitives (what could be changed).
2090 // There will probably be more hindrances here in other rendering paths
2091 // which could all be found - intention to do this would be: Use more
2092 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2094 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2095 // and embed into a TransformPrimitive2D containing the transformation.
2097 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2098 // && rLineWidths > 0.0) creates polygon fill infos that are not reusable
2099 // for the fill case (see ::drawPolyLine below) - thus we would need a
2100 // bool and/or two system-dependent paths buffered - doable, but complicated.
2102 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2103 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2104 // (at least for now...)
2107 pGraphicsPath
.reset(new Gdiplus::GraphicsPath());
2109 for(sal_uInt32
a(0); a
< nCount
; a
++)
2113 // #i101491# not needed for first run
2114 pGraphicsPath
->StartFigure();
2117 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2119 rPolyPolygon
.getB2DPolygon(a
),
2120 rObjectToDevice
, // not used due to the two 'false' values below, but to not forget later
2124 pGraphicsPath
->CloseFigure();
2127 // add to buffering mechanism
2128 rPolyPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2129 ImplGetSystemDependentDataManager(),
2134 if(mrParent
.getAntiAliasB2DDraw())
2136 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2140 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2143 if(mrParent
.isPrinter())
2146 // Normally GdiPlus should not be used for printing at all since printers cannot
2147 // print transparent filled polygon geometry and normally this does not happen
2148 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2149 // and no transparent parts should remain for printing. But this can be overridden
2150 // by the user and thus happens. This call can only come (currently) from
2151 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2152 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2153 // checked that there is *no* transformation set and estimated that a stable factor
2154 // dependent of the printer's DPI is used. Create and set a transformation here to
2156 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2157 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2159 // Now the transformation maybe/is already used (see above), so do
2160 // modify it without resetting to not destroy it.
2161 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2162 // we need - in our notation, would be a multiply from left to execute
2163 // current transform first and this scale last.
2164 // I tried to trigger this code using Print from the menu and various
2165 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2166 // more, feel free to remove it.
2167 // One more hint: This *may* also be needed now in ::drawPolyLine below
2168 // since it also uses transformations now.
2170 // aGraphics.ResetTransform();
2172 aGraphics
.ScaleTransform(
2173 Gdiplus::REAL(100.0) / aDpiX
,
2174 Gdiplus::REAL(100.0) / aDpiY
,
2175 Gdiplus::MatrixOrderAppend
);
2178 // use created or buffered data
2186 bool WinSalGraphicsImpl::drawPolyLine(
2187 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2188 const basegfx::B2DPolygon
& rPolygon
,
2189 double fTransparency
,
2190 const basegfx::B2DVector
& rLineWidths
,
2191 basegfx::B2DLineJoin eLineJoin
,
2192 css::drawing::LineCap eLineCap
,
2193 double fMiterMinimumAngle
,
2194 bool bPixelSnapHairline
)
2196 if(!mbPen
|| 0 == rPolygon
.count())
2201 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2202 const sal_uInt8 aTrans
= static_cast<sal_uInt8
>(basegfx::fround( 255 * (1.0 - fTransparency
) ));
2203 const Gdiplus::Color
aTestColor(aTrans
, maLineColor
.GetRed(), maLineColor
.GetGreen(), maLineColor
.GetBlue());
2204 Gdiplus::Pen
aPen(aTestColor
.GetValue(), Gdiplus::REAL(rLineWidths
.getX()));
2205 bool bNoLineJoin(false);
2207 // Set full (Object-to-Device) transformation - if used
2208 if(rObjectToDevice
.isIdentity())
2210 aGraphics
.ResetTransform();
2214 Gdiplus::Matrix aMatrix
;
2216 aMatrix
.SetElements(
2217 rObjectToDevice
.get(0, 0),
2218 rObjectToDevice
.get(1, 0),
2219 rObjectToDevice
.get(0, 1),
2220 rObjectToDevice
.get(1, 1),
2221 rObjectToDevice
.get(0, 2),
2222 rObjectToDevice
.get(1, 2));
2223 aGraphics
.SetTransform(&aMatrix
);
2228 case basegfx::B2DLineJoin::NONE
:
2230 if(basegfx::fTools::more(rLineWidths
.getX(), 0.0))
2236 case basegfx::B2DLineJoin::Bevel
:
2238 aPen
.SetLineJoin(Gdiplus::LineJoinBevel
);
2241 case basegfx::B2DLineJoin::Miter
:
2243 const Gdiplus::REAL
aMiterLimit(1.0/sin(fMiterMinimumAngle
/2.0));
2245 aPen
.SetMiterLimit(aMiterLimit
);
2246 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2247 // graphics, somewhere clipped in some distance from the edge point, dependent
2248 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2250 aPen
.SetLineJoin(Gdiplus::LineJoinMiterClipped
);
2253 case basegfx::B2DLineJoin::Round
:
2255 aPen
.SetLineJoin(Gdiplus::LineJoinRound
);
2262 default: /*css::drawing::LineCap_BUTT*/
2267 case css::drawing::LineCap_ROUND
:
2269 aPen
.SetStartCap(Gdiplus::LineCapRound
);
2270 aPen
.SetEndCap(Gdiplus::LineCapRound
);
2273 case css::drawing::LineCap_SQUARE
:
2275 aPen
.SetStartCap(Gdiplus::LineCapSquare
);
2276 aPen
.SetEndCap(Gdiplus::LineCapSquare
);
2281 // prepare local instance of Gdiplus::GraphicsPath
2282 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2284 // try to access buffered data
2285 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2286 rPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2288 if(pSystemDependentData_GraphicsPath
)
2290 // check data validity
2291 if (pSystemDependentData_GraphicsPath
->getNoLineJoin() != bNoLineJoin
2292 || bPixelSnapHairline
/*tdf#124700*/)
2294 // data invalid, forget
2295 pSystemDependentData_GraphicsPath
.reset();
2299 if(pSystemDependentData_GraphicsPath
)
2301 // copy buffered data
2302 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2306 // fill data of buffered data
2307 pGraphicsPath
.reset(new Gdiplus::GraphicsPath());
2309 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2314 bPixelSnapHairline
);
2316 if(rPolygon
.isClosed() && !bNoLineJoin
)
2318 // #i101491# needed to create the correct line joins
2319 pGraphicsPath
->CloseFigure();
2322 // add to buffering mechanism
2323 if (!bPixelSnapHairline
/*tdf#124700*/)
2325 rPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2326 ImplGetSystemDependentDataManager(),
2332 if(mrParent
.getAntiAliasB2DDraw())
2334 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2338 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2341 if(mrParent
.isPrinter())
2343 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2344 // (look for 'One more hint: This *may* also be needed now in'...).
2345 // See comments in same spot above *urgently* before doing changes here,
2346 // these comments are *still fully valid* at this place (!)
2347 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2348 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2350 aGraphics
.ScaleTransform(
2351 Gdiplus::REAL(100.0) / aDpiX
,
2352 Gdiplus::REAL(100.0) / aDpiY
,
2353 Gdiplus::MatrixOrderAppend
);
2363 static void paintToGdiPlus(
2364 Gdiplus::Graphics
& rGraphics
,
2365 const SalTwoRect
& rTR
,
2366 Gdiplus::Bitmap
& rBitmap
)
2368 // only parts of source are used
2369 Gdiplus::PointF aDestPoints
[3];
2370 Gdiplus::ImageAttributes aAttributes
;
2372 // define target region as paralellogram
2373 aDestPoints
[0].X
= Gdiplus::REAL(rTR
.mnDestX
);
2374 aDestPoints
[0].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2375 aDestPoints
[1].X
= Gdiplus::REAL(rTR
.mnDestX
+ rTR
.mnDestWidth
);
2376 aDestPoints
[1].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2377 aDestPoints
[2].X
= Gdiplus::REAL(rTR
.mnDestX
);
2378 aDestPoints
[2].Y
= Gdiplus::REAL(rTR
.mnDestY
+ rTR
.mnDestHeight
);
2380 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2382 rGraphics
.DrawImage(
2386 Gdiplus::REAL(rTR
.mnSrcX
),
2387 Gdiplus::REAL(rTR
.mnSrcY
),
2388 Gdiplus::REAL(rTR
.mnSrcWidth
),
2389 Gdiplus::REAL(rTR
.mnSrcHeight
),
2394 static void setInterpolationMode(
2395 Gdiplus::Graphics
& rGraphics
,
2401 const bool bSameWidth(rSrcWidth
== rDestWidth
);
2402 const bool bSameHeight(rSrcHeight
== rDestHeight
);
2404 if(bSameWidth
&& bSameHeight
)
2406 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeInvalid
);
2408 else if(rDestWidth
> rSrcWidth
&& rDestHeight
> rSrcHeight
)
2410 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2412 else if(rDestWidth
< rSrcWidth
&& rDestHeight
< rSrcHeight
)
2414 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeBicubic
);
2418 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2422 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect
& rTR
, const SalBitmap
& rSrcBitmap
)
2424 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2426 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2428 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2429 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap());
2433 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2435 setInterpolationMode(
2454 bool WinSalGraphicsImpl::blendBitmap(
2461 bool WinSalGraphicsImpl::blendAlphaBitmap(
2470 bool WinSalGraphicsImpl::drawAlphaBitmap(
2471 const SalTwoRect
& rTR
,
2472 const SalBitmap
& rSrcBitmap
,
2473 const SalBitmap
& rAlphaBmp
)
2475 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2477 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2478 assert(dynamic_cast<const WinSalBitmap
*>(&rAlphaBmp
));
2480 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2481 const WinSalBitmap
& rSalAlpha
= static_cast< const WinSalBitmap
& >(rAlphaBmp
);
2482 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(&rSalAlpha
));
2486 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2488 setInterpolationMode(
2507 bool WinSalGraphicsImpl::drawTransformedBitmap(
2508 const basegfx::B2DPoint
& rNull
,
2509 const basegfx::B2DPoint
& rX
,
2510 const basegfx::B2DPoint
& rY
,
2511 const SalBitmap
& rSourceBitmap
,
2512 const SalBitmap
* pAlphaBitmap
)
2514 assert(dynamic_cast<const WinSalBitmap
*>(&rSourceBitmap
));
2515 assert(!pAlphaBitmap
|| dynamic_cast<const WinSalBitmap
*>(pAlphaBitmap
));
2517 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSourceBitmap
);
2518 const WinSalBitmap
* pSalAlpha
= static_cast< const WinSalBitmap
* >(pAlphaBitmap
);
2519 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(pSalAlpha
));
2523 const long nSrcWidth(aARGB
->GetWidth());
2524 const long nSrcHeight(aARGB
->GetHeight());
2526 if(nSrcWidth
&& nSrcHeight
)
2528 const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX
- rNull
).getLength()));
2529 const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY
- rNull
).getLength()));
2531 if(nDestWidth
&& nDestHeight
)
2533 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2534 Gdiplus::PointF aDestPoints
[3];
2535 Gdiplus::ImageAttributes aAttributes
;
2537 setInterpolationMode(
2544 // this mode is only capable of drawing the whole bitmap to a paralellogram
2545 aDestPoints
[0].X
= Gdiplus::REAL(rNull
.getX());
2546 aDestPoints
[0].Y
= Gdiplus::REAL(rNull
.getY());
2547 aDestPoints
[1].X
= Gdiplus::REAL(rX
.getX());
2548 aDestPoints
[1].Y
= Gdiplus::REAL(rX
.getY());
2549 aDestPoints
[2].X
= Gdiplus::REAL(rY
.getX());
2550 aDestPoints
[2].Y
= Gdiplus::REAL(rY
.getY());
2552 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2554 aGraphics
.DrawImage(
2560 Gdiplus::REAL(nSrcWidth
),
2561 Gdiplus::REAL(nSrcHeight
),
2573 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon
& /*rPolygon*/,
2574 const Gradient
& /*rGradient*/)
2579 bool WinSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey
& /*rControlCacheKey*/, int /*nX*/, int /*nY*/)
2584 bool WinSalGraphicsImpl::RenderAndCacheNativeControl(OpenGLCompatibleDC
& /*rWhite*/, OpenGLCompatibleDC
& /*rBlack*/,
2585 int /*nX*/, int /*nY*/ , ControlCacheKey
& /*aControlCacheKey*/)
2590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */