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 <outdata.hxx>
50 #include <win/salids.hrc>
51 #include <ControlCacheKey.hxx>
55 #define min(a,b) (((a) < (b)) ? (a) : (b))
58 #define max(a,b) (((a) > (b)) ? (a) : (b))
65 #include <gdiplusenums.h>
66 #include <gdipluscolor.h>
70 #define SAL_POLYPOLYCOUNT_STACKBUF 8
71 #define SAL_POLYPOLYPOINTS_STACKBUF 64
73 #define SAL_POLY_STACKBUF 32
77 // #100127# Fill point and flag memory from array of points which
78 // might also contain bezier control points for the PolyDraw() GDI method
79 // Make sure pWinPointAry and pWinFlagAry are big enough
80 void ImplPreparePolyDraw( bool bCloseFigures
,
82 const sal_uInt32
* pPoints
,
83 const Point
* const* pPtAry
,
84 const PolyFlags
* const* pFlgAry
,
89 for( nCurrPoly
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
91 const Point
* pCurrPoint
= *pPtAry
++;
92 const PolyFlags
* pCurrFlag
= *pFlgAry
++;
93 const sal_uInt32 nCurrPoints
= *pPoints
++;
94 const bool bHaveFlagArray( pCurrFlag
);
100 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
102 *pWinFlagAry
++ = PT_MOVETO
;
105 for( nCurrPoint
=1; nCurrPoint
<nCurrPoints
; )
107 // #102067# Check existence of flag array
108 if( bHaveFlagArray
&&
109 ( nCurrPoint
+ 2 ) < nCurrPoints
)
111 PolyFlags
P4( pCurrFlag
[ 2 ] );
113 if( ( PolyFlags::Control
== pCurrFlag
[ 0 ] ) &&
114 ( PolyFlags::Control
== pCurrFlag
[ 1 ] ) &&
115 ( PolyFlags::Normal
== P4
|| PolyFlags::Smooth
== P4
|| PolyFlags::Symmetric
== P4
) )
118 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
120 *pWinFlagAry
++ = PT_BEZIERTO
;
123 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
125 *pWinFlagAry
++ = PT_BEZIERTO
;
128 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
130 *pWinFlagAry
++ = PT_BEZIERTO
;
138 // regular line point
139 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
141 *pWinFlagAry
++ = PT_LINETO
;
148 pWinFlagAry
[-1] |= PT_CLOSEFIGURE
;
153 Color
ImplGetROPColor( SalROPColor nROPColor
)
156 if ( nROPColor
== SalROPColor::N0
)
157 nColor
= Color( 0, 0, 0 );
159 nColor
= Color( 255, 255, 255 );
163 bool IsDitherColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
165 constexpr sal_uInt8 DITHER_PAL_DELTA
= 51;
167 return !(nRed
% DITHER_PAL_DELTA
) &&
168 !(nGreen
% DITHER_PAL_DELTA
) &&
169 !(nBlue
% DITHER_PAL_DELTA
);
172 bool IsPaletteColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
174 static const PALETTEENTRY aImplSalSysPalEntryAry
[] =
179 { 0, 0x80, 0x80, 0 },
181 { 0x80, 0, 0x80, 0 },
182 { 0x80, 0x80, 0, 0 },
183 { 0x80, 0x80, 0x80, 0 },
184 { 0xC0, 0xC0, 0xC0, 0 },
187 { 0, 0xFF, 0xFF, 0 },
189 { 0xFF, 0, 0xFF, 0 },
190 { 0xFF, 0xFF, 0, 0 },
191 { 0xFF, 0xFF, 0xFF, 0 }
194 for (const auto& rPalEntry
: aImplSalSysPalEntryAry
)
196 if(rPalEntry
.peRed
== nRed
&&
197 rPalEntry
.peGreen
== nGreen
&&
198 rPalEntry
.peBlue
== nBlue
)
207 bool IsExtraColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
209 return (nRed
== 0) && (nGreen
== 184) && (nBlue
== 255);
212 bool ImplIsPaletteEntry(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
214 return IsDitherColor(nRed
, nGreen
, nBlue
) ||
215 IsPaletteColor(nRed
, nGreen
, nBlue
) ||
216 IsExtraColor(nRed
, nGreen
, nBlue
);
221 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics
& rParent
):
233 WinSalGraphicsImpl::~WinSalGraphicsImpl()
244 DeleteBrush( mhBrush
);
248 void WinSalGraphicsImpl::Init()
252 void WinSalGraphicsImpl::freeResources()
256 bool WinSalGraphicsImpl::drawEPS(tools::Long
, tools::Long
, tools::Long
, tools::Long
, void*, sal_uInt32
)
261 void WinSalGraphicsImpl::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
267 hSrcDC
= static_cast<WinSalGraphics
*>(pSrcGraphics
)->getHDC();
269 hSrcDC
= mrParent
.getHDC();
276 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
277 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
279 BitBlt( mrParent
.getHDC(),
280 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
281 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
283 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
288 int nOldStretchMode
= SetStretchBltMode( mrParent
.getHDC(), STRETCH_DELETESCANS
);
289 StretchBlt( mrParent
.getHDC(),
290 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
291 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
293 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
294 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
296 SetStretchBltMode( mrParent
.getHDC(), nOldStretchMode
);
303 void MakeInvisibleArea(const RECT
& rSrcRect
,
304 int nLeft
, int nTop
, int nRight
, int nBottom
,
305 HRGN
& rhInvalidateRgn
)
307 if (!rhInvalidateRgn
)
309 rhInvalidateRgn
= CreateRectRgnIndirect(&rSrcRect
);
312 ScopedHRGN
hTempRgn(CreateRectRgn(nLeft
, nTop
, nRight
, nBottom
));
313 CombineRgn(rhInvalidateRgn
, rhInvalidateRgn
, hTempRgn
.get(), RGN_DIFF
);
316 void ImplCalcOutSideRgn( const RECT
& rSrcRect
,
317 int nLeft
, int nTop
, int nRight
, int nBottom
,
318 HRGN
& rhInvalidateRgn
)
320 // calculate area outside the visible region
321 if (rSrcRect
.left
< nLeft
)
323 MakeInvisibleArea(rSrcRect
, -31999, 0, nLeft
, 31999, rhInvalidateRgn
);
325 if (rSrcRect
.top
< nTop
)
327 MakeInvisibleArea(rSrcRect
, 0, -31999, 31999, nTop
, rhInvalidateRgn
);
329 if (rSrcRect
.right
> nRight
)
331 MakeInvisibleArea(rSrcRect
, nRight
, 0, 31999, 31999, rhInvalidateRgn
);
333 if (rSrcRect
.bottom
> nBottom
)
335 MakeInvisibleArea(rSrcRect
, 0, nBottom
, 31999, 31999, rhInvalidateRgn
);
341 void WinSalGraphicsImpl::copyArea( tools::Long nDestX
, tools::Long nDestY
,
342 tools::Long nSrcX
, tools::Long nSrcY
,
343 tools::Long nSrcWidth
, tools::Long nSrcHeight
,
344 bool bWindowInvalidate
)
346 bool bRestoreClipRgn
= false;
347 HRGN hOldClipRgn
= nullptr;
348 int nOldClipRgnType
= ERROR
;
349 HRGN hInvalidateRgn
= nullptr;
351 // do we have to invalidate also the overlapping regions?
352 if ( bWindowInvalidate
&& mrParent
.isWindow() )
354 // compute and invalidate those parts that were either off-screen or covered by other windows
355 // while performing the above BitBlt
356 // those regions then have to be invalidated as they contain useless/wrong data
364 // restrict srcRect to this window (calc intersection)
365 aSrcRect
.left
= static_cast<int>(nSrcX
);
366 aSrcRect
.top
= static_cast<int>(nSrcY
);
367 aSrcRect
.right
= aSrcRect
.left
+static_cast<int>(nSrcWidth
);
368 aSrcRect
.bottom
= aSrcRect
.top
+static_cast<int>(nSrcHeight
);
369 GetClientRect( mrParent
.gethWnd(), &aClipRect
);
370 if ( IntersectRect( &aSrcRect
, &aSrcRect
, &aClipRect
) )
372 // transform srcRect to screen coordinates
376 ClientToScreen( mrParent
.gethWnd(), &aPt
);
377 aSrcRect
.left
+= aPt
.x
;
378 aSrcRect
.top
+= aPt
.y
;
379 aSrcRect
.right
+= aPt
.x
;
380 aSrcRect
.bottom
+= aPt
.y
;
381 hInvalidateRgn
= nullptr;
383 // compute the parts that are off screen (ie invisible)
385 ImplSalGetWorkArea( nullptr, &theScreen
, nullptr ); // find the screen area taking multiple monitors into account
386 ImplCalcOutSideRgn( aSrcRect
, theScreen
.left
, theScreen
.top
, theScreen
.right
, theScreen
.bottom
, hInvalidateRgn
);
388 // calculate regions that are covered by other windows
389 HRGN hTempRgn2
= nullptr;
390 HWND hWndTopWindow
= mrParent
.gethWnd();
391 // Find the TopLevel Window, because only Windows which are in
392 // in the foreground of our TopLevel window must be considered
393 if ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
)
395 RECT aTempRect3
= aSrcRect
;
398 hWndTopWindow
= ::GetParent( hWndTopWindow
);
400 // Test if the Parent clips our window
401 GetClientRect( hWndTopWindow
, &aTempRect
);
405 ClientToScreen( hWndTopWindow
, &aPt2
);
406 aTempRect
.left
+= aPt2
.x
;
407 aTempRect
.top
+= aPt2
.y
;
408 aTempRect
.right
+= aPt2
.x
;
409 aTempRect
.bottom
+= aPt2
.y
;
410 IntersectRect( &aTempRect3
, &aTempRect3
, &aTempRect
);
412 while ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
);
414 // If one or more Parents clip our window, then we must
415 // calculate the outside area
416 if ( !EqualRect( &aSrcRect
, &aTempRect3
) )
418 ImplCalcOutSideRgn( aSrcRect
,
419 aTempRect3
.left
, aTempRect3
.top
,
420 aTempRect3
.right
, aTempRect3
.bottom
,
424 // retrieve the top-most (z-order) child window
425 hWnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
428 if ( hWnd
== hWndTopWindow
)
430 if ( IsWindowVisible( hWnd
) && !IsIconic( hWnd
) )
432 GetWindowRect( hWnd
, &aTempRect
);
433 if ( IntersectRect( &aTempRect2
, &aSrcRect
, &aTempRect
) )
435 // hWnd covers part or all of aSrcRect
436 if ( !hInvalidateRgn
)
437 hInvalidateRgn
= CreateRectRgnIndirect( &aSrcRect
);
439 // get full bounding box of hWnd
440 hTempRgn
= CreateRectRgnIndirect( &aTempRect
);
442 // get region of hWnd (the window may be shaped)
444 hTempRgn2
= CreateRectRgn( 0, 0, 0, 0 );
445 int nRgnType
= GetWindowRgn( hWnd
, hTempRgn2
);
446 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
448 // convert window region to screen coordinates
449 OffsetRgn( hTempRgn2
, aTempRect
.left
, aTempRect
.top
);
450 // and intersect with the window's bounding box
451 CombineRgn( hTempRgn
, hTempRgn
, hTempRgn2
, RGN_AND
);
453 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
454 CombineRgn( hInvalidateRgn
, hInvalidateRgn
, hTempRgn
, RGN_DIFF
);
455 DeleteRegion( hTempRgn
);
458 // retrieve the next window in the z-order, i.e. the window below hwnd
459 hWnd
= GetWindow( hWnd
, GW_HWNDNEXT
);
462 DeleteRegion( hTempRgn2
);
463 if ( hInvalidateRgn
)
465 // hInvalidateRgn contains the fully visible parts of the original srcRect
466 hTempRgn
= CreateRectRgnIndirect( &aSrcRect
);
467 // subtract it from the original rect to get the occluded parts
468 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_DIFF
);
469 DeleteRegion( hTempRgn
);
471 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
473 // move the occluded parts to the destination pos
474 int nOffX
= static_cast<int>(nDestX
-nSrcX
);
475 int nOffY
= static_cast<int>(nDestY
-nSrcY
);
476 OffsetRgn( hInvalidateRgn
, nOffX
-aPt
.x
, nOffY
-aPt
.y
);
478 // by excluding hInvalidateRgn from the system's clip region
479 // we will prevent bitblt from copying useless data
480 // especially now shadows from overlapping windows will appear (#i36344)
481 hOldClipRgn
= CreateRectRgn( 0, 0, 0, 0 );
482 nOldClipRgnType
= GetClipRgn( mrParent
.getHDC(), hOldClipRgn
);
484 bRestoreClipRgn
= true; // indicate changed clipregion and force invalidate
485 ExtSelectClipRgn( mrParent
.getHDC(), hInvalidateRgn
, RGN_DIFF
);
491 BitBlt( mrParent
.getHDC(),
492 static_cast<int>(nDestX
), static_cast<int>(nDestY
),
493 static_cast<int>(nSrcWidth
), static_cast<int>(nSrcHeight
),
495 static_cast<int>(nSrcX
), static_cast<int>(nSrcY
),
498 if( bRestoreClipRgn
)
500 // restore old clip region
501 if( nOldClipRgnType
!= ERROR
)
502 SelectClipRgn( mrParent
.getHDC(), hOldClipRgn
);
503 DeleteRegion( hOldClipRgn
);
505 // invalidate regions that were not copied
506 bool bInvalidate
= true;
508 // Combine Invalidate vcl::Region with existing ClipRegion
509 HRGN hTempRgn
= CreateRectRgn( 0, 0, 0, 0 );
510 if ( GetClipRgn( mrParent
.getHDC(), hTempRgn
) == 1 )
512 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_AND
);
513 if ( (nRgnType
== ERROR
) || (nRgnType
== NULLREGION
) )
516 DeleteRegion( hTempRgn
);
520 InvalidateRgn( mrParent
.gethWnd(), hInvalidateRgn
, TRUE
);
521 // here we only initiate an update if this is the MainThread,
522 // so that there is no deadlock when handling the Paint event,
523 // as the SolarMutex is already held by this Thread
524 SalData
* pSalData
= GetSalData();
525 DWORD nCurThreadId
= GetCurrentThreadId();
526 if ( pSalData
->mnAppThreadId
== nCurThreadId
)
527 UpdateWindow( mrParent
.gethWnd() );
530 DeleteRegion( hInvalidateRgn
);
537 void ImplDrawBitmap( HDC hDC
, const SalTwoRect
& rPosAry
, const WinSalBitmap
& rSalBitmap
,
538 bool bPrinter
, int nDrawMode
)
543 HBITMAP hDrawDDB
= rSalBitmap
.ImplGethDDB();
544 std::unique_ptr
<WinSalBitmap
> xTmpSalBmp
;
545 bool bPrintDDB
= ( bPrinter
&& hDrawDDB
);
549 xTmpSalBmp
.reset(new WinSalBitmap
);
550 xTmpSalBmp
->Create( rSalBitmap
, rSalBitmap
.GetBitCount() );
551 hDrawDIB
= xTmpSalBmp
->ImplGethDIB();
554 hDrawDIB
= rSalBitmap
.ImplGethDIB();
558 PBITMAPINFO pBI
= static_cast<PBITMAPINFO
>(GlobalLock( hDrawDIB
));
559 PBYTE pBits
= reinterpret_cast<PBYTE
>(pBI
) + pBI
->bmiHeader
.biSize
+
560 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB
) * sizeof( RGBQUAD
);
561 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
564 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
565 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
566 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(pBI
->bmiHeader
.biHeight
- rPosAry
.mnSrcHeight
- rPosAry
.mnSrcY
),
567 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
568 pBits
, pBI
, DIB_RGB_COLORS
, nDrawMode
);
570 GlobalUnlock( hDrawDIB
);
571 SetStretchBltMode( hDC
, nOldStretchMode
);
573 else if( hDrawDDB
&& !bPrintDDB
)
575 ScopedCachedHDC
<CACHED_HDC_DRAW
> hBmpDC(hDrawDDB
);
577 COLORREF nOldBkColor
= RGB(0xFF,0xFF,0xFF);
578 COLORREF nOldTextColor
= RGB(0,0,0);
579 bool bMono
= ( rSalBitmap
.GetBitCount() == 1 );
583 COLORREF nBkColor
= RGB( 0xFF, 0xFF, 0xFF );
584 COLORREF nTextColor
= RGB( 0x00, 0x00, 0x00 );
585 //fdo#33455 handle 1 bit depth pngs with palette entries
586 //to set fore/back colors
587 if (BitmapBuffer
* pBitmapBuffer
= const_cast<WinSalBitmap
&>(rSalBitmap
).AcquireBuffer(BitmapAccessMode::Info
))
589 const BitmapPalette
& rPalette
= pBitmapBuffer
->maPalette
;
590 if (rPalette
.GetEntryCount() == 2)
592 Color nCol
= rPalette
[0];
593 nTextColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
595 nBkColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
597 const_cast<WinSalBitmap
&>(rSalBitmap
).ReleaseBuffer(pBitmapBuffer
, BitmapAccessMode::Info
);
599 nOldBkColor
= SetBkColor( hDC
, nBkColor
);
600 nOldTextColor
= ::SetTextColor( hDC
, nTextColor
);
603 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
604 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
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
),
615 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
618 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
619 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
621 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
622 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
625 SetStretchBltMode( hDC
, nOldStretchMode
);
630 SetBkColor( hDC
, nOldBkColor
);
631 ::SetTextColor( hDC
, nOldTextColor
);
639 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
641 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
645 // only paint direct when no scaling and no MapMode, else the
646 // more expensive conversions may be done for short-time Bitmap/BitmapEx
647 // used for buffering only
648 if(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&& rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
)
650 bTryDirectPaint
= false;
654 // try to draw using GdiPlus directly
655 if(bTryDirectPaint
&& TryDrawBitmapGDIPlus(rPosAry
, rSalBitmap
))
660 // fall back old stuff
661 assert(dynamic_cast<const WinSalBitmap
*>(&rSalBitmap
));
663 ImplDrawBitmap(mrParent
.getHDC(), rPosAry
, static_cast<const WinSalBitmap
&>(rSalBitmap
),
664 mrParent
.isPrinter(),
665 mbXORMode
? SRCINVERT
: SRCCOPY
);
668 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect
& rPosAry
,
669 const SalBitmap
& rSSalBitmap
,
670 const SalBitmap
& rSTransparentBitmap
)
672 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
673 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
675 // try to draw using GdiPlus directly
676 if(bTryDirectPaint
&& drawAlphaBitmap(rPosAry
, rSSalBitmap
, rSTransparentBitmap
))
681 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
682 assert(dynamic_cast<const WinSalBitmap
*>(&rSTransparentBitmap
));
684 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
685 const WinSalBitmap
& rTransparentBitmap
= static_cast<const WinSalBitmap
&>(rSTransparentBitmap
);
687 SalTwoRect aPosAry
= rPosAry
;
688 int nDstX
= static_cast<int>(aPosAry
.mnDestX
);
689 int nDstY
= static_cast<int>(aPosAry
.mnDestY
);
690 int nDstWidth
= static_cast<int>(aPosAry
.mnDestWidth
);
691 int nDstHeight
= static_cast<int>(aPosAry
.mnDestHeight
);
692 HDC hDC
= mrParent
.getHDC();
694 ScopedHBITMAP hMemBitmap
;
695 ScopedHBITMAP hMaskBitmap
;
697 if( ( nDstWidth
> CACHED_HDC_DEFEXT
) || ( nDstHeight
> CACHED_HDC_DEFEXT
) )
699 hMemBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
700 hMaskBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
703 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(hMemBitmap
.get());
704 ScopedCachedHDC
<CACHED_HDC_2
> hMaskDC(hMaskBitmap
.get());
706 aPosAry
.mnDestX
= aPosAry
.mnDestY
= 0;
707 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hDC
, nDstX
, nDstY
, SRCCOPY
);
709 // WIN/WNT seems to have a minor problem mapping the correct color of the
710 // mask to the palette if we draw the DIB directly ==> draw DDB
711 if( ( GetBitCount() <= 8 ) && rTransparentBitmap
.ImplGethDIB() && rTransparentBitmap
.GetBitCount() == 1 )
715 if( aTmp
.Create( rTransparentBitmap
, &mrParent
) )
716 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, aTmp
, false, SRCCOPY
);
719 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rTransparentBitmap
, false, SRCCOPY
);
721 // now MemDC contains background, MaskDC the transparency mask
723 // #105055# Respect XOR mode
726 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
727 // now MaskDC contains the bitmap area with black background
728 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCINVERT
);
729 // now MemDC contains background XORed bitmap area ontop
733 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCAND
);
734 // now MemDC contains background with masked-out bitmap area
735 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
736 // now MaskDC contains the bitmap area with black background
737 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCPAINT
);
738 // now MemDC contains background and bitmap merged together
741 BitBlt( hDC
, nDstX
, nDstY
, nDstWidth
, nDstHeight
, hMemDC
.get(), 0, 0, SRCCOPY
);
744 bool WinSalGraphicsImpl::drawAlphaRect( tools::Long nX
, tools::Long nY
, tools::Long nWidth
,
745 tools::Long nHeight
, sal_uInt8 nTransparency
)
747 if( mbPen
|| !mbBrush
|| mbXORMode
)
748 return false; // can only perform solid fills without XOR.
750 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(nullptr);
751 SetPixel( hMemDC
.get(), int(0), int(0), mnBrushColor
);
753 BLENDFUNCTION aFunc
= {
756 sal::static_int_cast
<sal_uInt8
>(255 - 255L*nTransparency
/100),
760 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
762 bool bRet
= GdiAlphaBlend(mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
,
763 hMemDC
.get(), 0,0,1,1,
769 void WinSalGraphicsImpl::drawMask(const SalTwoRect
& rPosAry
,
770 const SalBitmap
& rSSalBitmap
,
773 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
775 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
777 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
779 SalTwoRect aPosAry
= rPosAry
;
780 const HDC hDC
= mrParent
.getHDC();
782 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(RGB(nMaskColor
.GetRed(),
783 nMaskColor
.GetGreen(),
784 nMaskColor
.GetBlue())));
786 // WIN/WNT seems to have a minor problem mapping the correct color of the
787 // mask to the palette if we draw the DIB directly ==> draw DDB
788 if( ( GetBitCount() <= 8 ) && rSalBitmap
.ImplGethDIB() && rSalBitmap
.GetBitCount() == 1 )
792 if( aTmp
.Create( rSalBitmap
, &mrParent
) )
793 ImplDrawBitmap( hDC
, aPosAry
, aTmp
, false, 0x00B8074AUL
);
796 ImplDrawBitmap( hDC
, aPosAry
, rSalBitmap
, false, 0x00B8074AUL
);
799 std::shared_ptr
<SalBitmap
> WinSalGraphicsImpl::getBitmap( tools::Long nX
, tools::Long nY
, tools::Long nDX
, tools::Long nDY
)
801 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
803 std::shared_ptr
<WinSalBitmap
> pSalBitmap
;
805 nDX
= std::abs( nDX
);
806 nDY
= std::abs( nDY
);
808 HDC hDC
= mrParent
.getHDC();
809 HBITMAP hBmpBitmap
= CreateCompatibleBitmap( hDC
, nDX
, nDY
);
813 ScopedCachedHDC
<CACHED_HDC_1
> hBmpDC(hBmpBitmap
);
815 bRet
= BitBlt(hBmpDC
.get(), 0, 0,
816 static_cast<int>(nDX
), static_cast<int>(nDY
), hDC
,
817 static_cast<int>(nX
), static_cast<int>(nY
), SRCCOPY
) ? TRUE
: FALSE
;
822 pSalBitmap
= std::make_shared
<WinSalBitmap
>();
824 if( !pSalBitmap
->Create( hBmpBitmap
, false, false ) )
831 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
832 DeleteBitmap( hBmpBitmap
);
838 Color
WinSalGraphicsImpl::getPixel( tools::Long nX
, tools::Long nY
)
840 COLORREF aWinCol
= ::GetPixel( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
) );
842 if ( CLR_INVALID
== aWinCol
)
843 return Color( 0, 0, 0 );
845 return Color( GetRValue( aWinCol
),
846 GetGValue( aWinCol
),
847 GetBValue( aWinCol
) );
853 HBRUSH
Get50PercentBrush()
855 SalData
* pSalData
= GetSalData();
856 if ( !pSalData
->mh50Brush
)
858 if ( !pSalData
->mh50Bmp
)
859 pSalData
->mh50Bmp
= ImplLoadSalBitmap( SAL_RESID_BITMAP_50
);
860 pSalData
->mh50Brush
= CreatePatternBrush( pSalData
->mh50Bmp
);
863 return pSalData
->mh50Brush
;
868 void WinSalGraphicsImpl::invert( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
, SalInvert nFlags
)
870 if ( nFlags
& SalInvert::TrackFrame
)
872 HPEN hDotPen
= CreatePen( PS_DOT
, 0, 0 );
873 HPEN hOldPen
= SelectPen( mrParent
.getHDC(), hDotPen
);
874 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), GetStockBrush( NULL_BRUSH
) );
875 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
877 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
879 SetROP2( mrParent
.getHDC(), nOldROP
);
880 SelectPen( mrParent
.getHDC(), hOldPen
);
881 SelectBrush( mrParent
.getHDC(), hOldBrush
);
882 DeletePen( hDotPen
);
884 else if ( nFlags
& SalInvert::N50
)
886 COLORREF nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
887 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), Get50PercentBrush() );
888 PatBlt( mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
, PATINVERT
);
889 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
890 SelectBrush( mrParent
.getHDC(), hOldBrush
);
895 aRect
.left
= static_cast<int>(nX
);
896 aRect
.top
= static_cast<int>(nY
);
897 aRect
.right
= static_cast<int>(nX
)+nWidth
;
898 aRect
.bottom
= static_cast<int>(nY
)+nHeight
;
899 ::InvertRect( mrParent
.getHDC(), &aRect
);
903 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints
, const Point
* pPtAry
, SalInvert nSalFlags
)
908 HBRUSH hOldBrush
= nullptr;
909 COLORREF nOldTextColor
RGB(0,0,0);
910 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
912 if ( nSalFlags
& SalInvert::TrackFrame
)
913 hPen
= CreatePen( PS_DOT
, 0, 0 );
917 if ( nSalFlags
& SalInvert::N50
)
918 hBrush
= Get50PercentBrush();
920 hBrush
= GetStockBrush( BLACK_BRUSH
);
922 hPen
= GetStockPen( NULL_PEN
);
923 nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
924 hOldBrush
= SelectBrush( mrParent
.getHDC(), hBrush
);
926 hOldPen
= SelectPen( mrParent
.getHDC(), hPen
);
928 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
929 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
930 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
932 // for Windows 95 and its maximum number of points
933 if ( nSalFlags
& SalInvert::TrackFrame
)
935 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
936 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
940 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
941 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
944 SetROP2( mrParent
.getHDC(), nOldROP
);
945 SelectPen( mrParent
.getHDC(), hOldPen
);
947 if ( nSalFlags
& SalInvert::TrackFrame
)
951 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
952 SelectBrush( mrParent
.getHDC(), hOldBrush
);
956 sal_uInt16
WinSalGraphicsImpl::GetBitCount() const
958 return static_cast<sal_uInt16
>(GetDeviceCaps( mrParent
.getHDC(), BITSPIXEL
));
961 tools::Long
WinSalGraphicsImpl::GetGraphicsWidth() const
963 if( mrParent
.gethWnd() && IsWindow( mrParent
.gethWnd() ) )
965 WinSalFrame
* pFrame
= GetWindowPtr( mrParent
.gethWnd() );
968 if( pFrame
->maGeometry
.nWidth
)
969 return pFrame
->maGeometry
.nWidth
;
972 // TODO: perhaps not needed, maGeometry should always be up-to-date
974 GetClientRect( mrParent
.gethWnd(), &aRect
);
983 void WinSalGraphicsImpl::ResetClipRegion()
985 if ( mrParent
.mhRegion
)
987 DeleteRegion( mrParent
.mhRegion
);
988 mrParent
.mhRegion
= nullptr;
991 SelectClipRgn( mrParent
.getHDC(), nullptr );
994 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon
& rCandidate
)
996 if(rCandidate
.areControlPointsUsed())
1001 const sal_uInt32
nPointCount(rCandidate
.count());
1008 const sal_uInt32
nEdgeCount(rCandidate
.isClosed() ? nPointCount
+ 1 : nPointCount
);
1009 basegfx::B2DPoint
aLast(rCandidate
.getB2DPoint(0));
1011 for(sal_uInt32
a(1); a
< nEdgeCount
; a
++)
1013 const sal_uInt32
nNextIndex(a
% nPointCount
);
1014 const basegfx::B2DPoint
aCurrent(rCandidate
.getB2DPoint(nNextIndex
));
1016 if(!basegfx::fTools::equal(aLast
.getX(), aCurrent
.getX()) && !basegfx::fTools::equal(aLast
.getY(), aCurrent
.getY()))
1027 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon
& rCandidate
)
1029 if(rCandidate
.areControlPointsUsed())
1034 for(auto const& rPolygon
: rCandidate
)
1036 if(!containsOnlyHorizontalAndVerticalEdges(rPolygon
))
1045 bool WinSalGraphicsImpl::setClipRegion( const vcl::Region
& i_rClip
)
1047 if ( mrParent
.mhRegion
)
1049 DeleteRegion( mrParent
.mhRegion
);
1050 mrParent
.mhRegion
= nullptr;
1053 bool bUsePolygon(i_rClip
.HasPolyPolygonOrB2DPolyPolygon());
1054 static bool bTryToAvoidPolygon(true);
1056 // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1057 // and only contains horizontal/vertical edges. In that case, use the fallback
1058 // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1059 // the correct polygon-to-RegionBand transformation.
1060 // Background is that when using the same Rectangle as rectangle or as Polygon
1061 // clip region will lead to different results; the polygon-based one will be
1062 // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1063 // again is because of the polygon-nature and it's classic handling when filling.
1064 // This also means that all cases which use a 'true' polygon-based incarnation of
1065 // a vcl::Region should know what they do - it may lead to repaint errors.
1066 if(bUsePolygon
&& bTryToAvoidPolygon
)
1068 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1070 if(!aPolyPolygon
.areControlPointsUsed())
1072 if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon
))
1074 bUsePolygon
= false;
1081 // #i122149# check the comment above to know that this may lead to potential repaint
1082 // problems. It may be solved (if needed) by scaling the polygon by one in X
1083 // and Y. Currently the workaround to only use it if really unavoidable will
1084 // solve most cases. When someone is really using polygon-based Regions he
1085 // should know what he is doing.
1086 // Added code to do that scaling to check if it works, testing it.
1087 const basegfx::B2DPolyPolygon
aPolyPolygon( i_rClip
.GetAsB2DPolyPolygon() );
1088 const sal_uInt32
nCount(aPolyPolygon
.count());
1092 std::vector
< POINT
> aPolyPoints
;
1093 aPolyPoints
.reserve( 1024 );
1094 std::vector
< INT
> aPolyCounts( nCount
, 0 );
1095 basegfx::B2DHomMatrix aExpand
;
1096 sal_uInt32
nTargetCount(0);
1097 static bool bExpandByOneInXandY(true);
1099 if(bExpandByOneInXandY
)
1101 const basegfx::B2DRange
aRangeS(aPolyPolygon
.getB2DRange());
1102 const basegfx::B2DRange
aRangeT(aRangeS
.getMinimum(), aRangeS
.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1103 aExpand
= basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS
, aRangeT
);
1106 for(auto const& rPolygon
: aPolyPolygon
)
1108 const basegfx::B2DPolygon
aPoly(
1109 basegfx::utils::adaptiveSubdivideByDistance(
1112 const sal_uInt32
nPoints(aPoly
.count());
1114 // tdf#40863 For CustomShapes there is a hack (see
1115 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1116 // with a single point in top-left and bottom-right corner
1117 // of the BoundRect to be able to determine the correct BoundRect
1118 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1119 // fails with polygons containing a single pixel, so clipping is
1120 // lost. For now, use only polygons with more than two points - the
1121 // ones that may have an area.
1122 // Note: polygons with one point which are curves may have an area,
1123 // but the polygon is already subdivided here, so no need to test
1127 aPolyCounts
[nTargetCount
] = nPoints
;
1130 for( sal_uInt32 b
= 0; b
< nPoints
; b
++ )
1132 basegfx::B2DPoint
aPt(aPoly
.getB2DPoint(b
));
1134 if(bExpandByOneInXandY
)
1136 aPt
= aExpand
* aPt
;
1140 // #i122149# do correct rounding
1141 aPOINT
.x
= basegfx::fround(aPt
.getX());
1142 aPOINT
.y
= basegfx::fround(aPt
.getY());
1143 aPolyPoints
.push_back( aPOINT
);
1150 mrParent
.mhRegion
= CreatePolyPolygonRgn( aPolyPoints
.data(), aPolyCounts
.data(), nTargetCount
, ALTERNATE
);
1156 RectangleVector aRectangles
;
1157 i_rClip
.GetRegionRectangles(aRectangles
);
1159 sal_uLong nRectBufSize
= sizeof(RECT
)*aRectangles
.size();
1160 if ( aRectangles
.size() < SAL_CLIPRECT_COUNT
)
1162 if ( !mrParent
.mpStdClipRgnData
)
1163 mrParent
.mpStdClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+(SAL_CLIPRECT_COUNT
*sizeof(RECT
))]);
1164 mrParent
.mpClipRgnData
= mrParent
.mpStdClipRgnData
;
1167 mrParent
.mpClipRgnData
= reinterpret_cast<RGNDATA
*>(new BYTE
[sizeof(RGNDATA
)-1+nRectBufSize
]);
1168 mrParent
.mpClipRgnData
->rdh
.dwSize
= sizeof( RGNDATAHEADER
);
1169 mrParent
.mpClipRgnData
->rdh
.iType
= RDH_RECTANGLES
;
1170 mrParent
.mpClipRgnData
->rdh
.nCount
= aRectangles
.size();
1171 mrParent
.mpClipRgnData
->rdh
.nRgnSize
= nRectBufSize
;
1172 RECT
* pBoundRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1173 SetRectEmpty( pBoundRect
);
1174 RECT
* pNextClipRect
= reinterpret_cast<RECT
*>(&(mrParent
.mpClipRgnData
->Buffer
));
1175 bool bFirstClipRect
= true;
1177 for (auto const& rectangle
: aRectangles
)
1179 const tools::Long
nW(rectangle
.GetWidth());
1180 const tools::Long
nH(rectangle
.GetHeight());
1184 const tools::Long
nRight(rectangle
.Left() + nW
);
1185 const tools::Long
nBottom(rectangle
.Top() + nH
);
1189 pBoundRect
->left
= rectangle
.Left();
1190 pBoundRect
->top
= rectangle
.Top();
1191 pBoundRect
->right
= nRight
;
1192 pBoundRect
->bottom
= nBottom
;
1193 bFirstClipRect
= false;
1197 if(rectangle
.Left() < pBoundRect
->left
)
1199 pBoundRect
->left
= static_cast<int>(rectangle
.Left());
1202 if(rectangle
.Top() < pBoundRect
->top
)
1204 pBoundRect
->top
= static_cast<int>(rectangle
.Top());
1207 if(nRight
> pBoundRect
->right
)
1209 pBoundRect
->right
= static_cast<int>(nRight
);
1212 if(nBottom
> pBoundRect
->bottom
)
1214 pBoundRect
->bottom
= static_cast<int>(nBottom
);
1218 pNextClipRect
->left
= static_cast<int>(rectangle
.Left());
1219 pNextClipRect
->top
= static_cast<int>(rectangle
.Top());
1220 pNextClipRect
->right
= static_cast<int>(nRight
);
1221 pNextClipRect
->bottom
= static_cast<int>(nBottom
);
1226 mrParent
.mpClipRgnData
->rdh
.nCount
--;
1227 mrParent
.mpClipRgnData
->rdh
.nRgnSize
-= sizeof( RECT
);
1231 // create clip region from ClipRgnData
1232 if(0 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1234 // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1235 // that contains no polygons or only empty ones (no width/height). This is
1236 // perfectly fine and we are done, except setting it (see end of method)
1238 else if(1 == mrParent
.mpClipRgnData
->rdh
.nCount
)
1240 RECT
* pRect
= &(mrParent
.mpClipRgnData
->rdh
.rcBound
);
1241 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
,
1242 pRect
->right
, pRect
->bottom
);
1244 else if(mrParent
.mpClipRgnData
->rdh
.nCount
> 1)
1246 sal_uLong nSize
= mrParent
.mpClipRgnData
->rdh
.nRgnSize
+sizeof(RGNDATAHEADER
);
1247 mrParent
.mhRegion
= ExtCreateRegion( nullptr, nSize
, mrParent
.mpClipRgnData
);
1249 // if ExtCreateRegion(...) is not supported
1250 if( !mrParent
.mhRegion
)
1252 RGNDATAHEADER
const & pHeader
= mrParent
.mpClipRgnData
->rdh
;
1254 if( pHeader
.nCount
)
1256 RECT
* pRect
= reinterpret_cast<RECT
*>(mrParent
.mpClipRgnData
->Buffer
);
1257 mrParent
.mhRegion
= CreateRectRgn( pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
);
1260 for( sal_uLong n
= 1; n
< pHeader
.nCount
; n
++, pRect
++ )
1262 ScopedHRGN
hRgn(CreateRectRgn(pRect
->left
, pRect
->top
, pRect
->right
, pRect
->bottom
));
1263 CombineRgn( mrParent
.mhRegion
, mrParent
.mhRegion
, hRgn
.get(), RGN_OR
);
1268 if ( mrParent
.mpClipRgnData
!= mrParent
.mpStdClipRgnData
)
1269 delete [] reinterpret_cast<BYTE
*>(mrParent
.mpClipRgnData
);
1273 if( mrParent
.mhRegion
)
1275 SelectClipRgn( mrParent
.getHDC(), mrParent
.mhRegion
);
1277 // debug code if you want to check range of the newly applied ClipRegion
1279 //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1285 // #i123585# See above, this is a valid case, execute it
1286 SelectClipRgn( mrParent
.getHDC(), nullptr );
1289 // #i123585# retval no longer dependent of mrParent.mhRegion, see TaskId comments above
1293 void WinSalGraphicsImpl::SetLineColor()
1295 ResetPen(GetStockPen(NULL_PEN
));
1302 void WinSalGraphicsImpl::SetLineColor(Color nColor
)
1304 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1307 bool bStockPen
= false;
1309 HPEN hNewPen
= SearchStockPen(nPenColor
);
1313 hNewPen
= MakePen(nColor
);
1318 mnPenColor
= nPenColor
;
1319 maLineColor
= nColor
;
1321 mbStockPen
= bStockPen
;
1324 HPEN
WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor
)
1326 // Only screen, because printer has problems, when we use stock objects.
1327 if (!mrParent
.isPrinter())
1329 const SalData
* pSalData
= GetSalData();
1331 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockPenCount
; i
++)
1333 if (nPenColor
== pSalData
->maStockPenColorAry
[i
])
1334 return pSalData
->mhStockPenAry
[i
];
1341 HPEN
WinSalGraphicsImpl::MakePen(Color nColor
)
1343 COLORREF nPenColor
= PALETTERGB(nColor
.GetRed(),
1347 if (!mrParent
.isPrinter())
1349 if (GetSalData()->mhDitherPal
&& ImplIsSysColorEntry(nColor
))
1351 nPenColor
= PALRGB_TO_RGB(nPenColor
);
1355 return CreatePen(PS_SOLID
, mrParent
.mnPenWidth
, nPenColor
);
1358 void WinSalGraphicsImpl::ResetPen(HPEN hNewPen
)
1360 HPEN hOldPen
= SelectPen(mrParent
.getHDC(), hNewPen
);
1371 mrParent
.mhDefPen
= hOldPen
;
1377 void WinSalGraphicsImpl::SetFillColor()
1379 ResetBrush(GetStockBrush(NULL_BRUSH
));
1383 mbStockBrush
= true;
1386 void WinSalGraphicsImpl::SetFillColor(Color nColor
)
1388 COLORREF nBrushColor
= PALETTERGB(nColor
.GetRed(),
1391 bool bStockBrush
= false;
1393 HBRUSH hNewBrush
= SearchStockBrush(nBrushColor
);
1397 hNewBrush
= MakeBrush(nColor
);
1399 ResetBrush(hNewBrush
);
1402 mnBrushColor
= nBrushColor
;
1403 maFillColor
= nColor
;
1405 mbStockBrush
= bStockBrush
;
1408 HBRUSH
WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor
)
1410 // Only screen, because printer has problems, when we use stock objects.
1411 if (!mrParent
.isPrinter())
1413 const SalData
* pSalData
= GetSalData();
1415 for (sal_uInt16 i
= 0; i
< pSalData
->mnStockBrushCount
; i
++)
1417 if (nBrushColor
== pSalData
->maStockBrushColorAry
[i
])
1418 return pSalData
->mhStockBrushAry
[i
];
1428 BYTE
GetDitherMappingValue(BYTE nVal
, BYTE nThres
, const SalData
* pSalData
)
1430 return (pSalData
->mpDitherDiff
[nVal
] > nThres
) ?
1431 pSalData
->mpDitherHigh
[nVal
] : pSalData
->mpDitherLow
[nVal
];
1434 HBRUSH
Make16BitDIBPatternBrush(Color nColor
)
1436 const SalData
* pSalData
= GetSalData();
1438 const BYTE nRed
= nColor
.GetRed();
1439 const BYTE nGreen
= nColor
.GetGreen();
1440 const BYTE nBlue
= nColor
.GetBlue();
1442 static const BYTE aOrdDither16Bit
[8][8] =
1444 { 0, 6, 1, 7, 0, 6, 1, 7 },
1445 { 4, 2, 5, 3, 4, 2, 5, 3 },
1446 { 1, 7, 0, 6, 1, 7, 0, 6 },
1447 { 5, 3, 4, 2, 5, 3, 4, 2 },
1448 { 0, 6, 1, 7, 0, 6, 1, 7 },
1449 { 4, 2, 5, 3, 4, 2, 5, 3 },
1450 { 1, 7, 0, 6, 1, 7, 0, 6 },
1451 { 5, 3, 4, 2, 5, 3, 4, 2 }
1454 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1456 for(int nY
= 0; nY
< 8; ++nY
)
1458 for(int nX
= 0; nX
< 8; ++nX
)
1460 const BYTE nThres
= aOrdDither16Bit
[nY
][nX
];
1461 *pTmp
++ = GetDitherMappingValue(nBlue
, nThres
, pSalData
);
1462 *pTmp
++ = GetDitherMappingValue(nGreen
, nThres
, pSalData
);
1463 *pTmp
++ = GetDitherMappingValue(nRed
, nThres
, pSalData
);
1467 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_RGB_COLORS
);
1470 HBRUSH
Make8BitDIBPatternBrush(Color nColor
)
1472 const SalData
* pSalData
= GetSalData();
1474 const BYTE nRed
= nColor
.GetRed();
1475 const BYTE nGreen
= nColor
.GetGreen();
1476 const BYTE nBlue
= nColor
.GetBlue();
1478 static const BYTE aOrdDither8Bit
[8][8] =
1480 { 0, 38, 9, 48, 2, 40, 12, 50 },
1481 { 25, 12, 35, 22, 28, 15, 37, 24 },
1482 { 6, 44, 3, 41, 8, 47, 5, 44 },
1483 { 32, 19, 28, 16, 34, 21, 31, 18 },
1484 { 1, 40, 11, 49, 0, 39, 10, 48 },
1485 { 27, 14, 36, 24, 26, 13, 36, 23 },
1486 { 8, 46, 4, 43, 7, 45, 4, 42 },
1487 { 33, 20, 30, 17, 32, 20, 29, 16 }
1490 BYTE
* pTmp
= pSalData
->mpDitherDIBData
;
1492 for (int nY
= 0; nY
< 8; ++nY
)
1494 for (int nX
= 0; nX
< 8; ++nX
)
1496 const BYTE nThres
= aOrdDither8Bit
[nY
][nX
];
1497 *pTmp
= GetDitherMappingValue(nRed
, nThres
, pSalData
) +
1498 GetDitherMappingValue(nGreen
, nThres
, pSalData
) * 6 +
1499 GetDitherMappingValue(nBlue
, nThres
, pSalData
) * 36;
1504 return CreateDIBPatternBrush(pSalData
->mhDitherDIB
, DIB_PAL_COLORS
);
1509 HBRUSH
WinSalGraphicsImpl::MakeBrush(Color nColor
)
1511 const SalData
* pSalData
= GetSalData();
1513 const BYTE nRed
= nColor
.GetRed();
1514 const BYTE nGreen
= nColor
.GetGreen();
1515 const BYTE nBlue
= nColor
.GetBlue();
1516 const COLORREF nBrushColor
= PALETTERGB(nRed
, nGreen
, nBlue
);
1518 if (mrParent
.isPrinter() || !pSalData
->mhDitherDIB
)
1519 return CreateSolidBrush(nBrushColor
);
1521 if (24 == reinterpret_cast<BITMAPINFOHEADER
*>(pSalData
->mpDitherDIB
)->biBitCount
)
1522 return Make16BitDIBPatternBrush(nColor
);
1524 if (ImplIsSysColorEntry(nColor
))
1525 return CreateSolidBrush(PALRGB_TO_RGB(nBrushColor
));
1527 if (ImplIsPaletteEntry(nRed
, nGreen
, nBlue
))
1528 return CreateSolidBrush(nBrushColor
);
1530 return Make8BitDIBPatternBrush(nColor
);
1533 void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush
)
1535 HBRUSH hOldBrush
= SelectBrush(mrParent
.getHDC(), hNewBrush
);
1541 DeleteBrush(mhBrush
);
1546 mrParent
.mhDefBrush
= hOldBrush
;
1549 mhBrush
= hNewBrush
;
1552 void WinSalGraphicsImpl::SetXORMode( bool bSet
, bool )
1555 ::SetROP2( mrParent
.getHDC(), bSet
? R2_XORPEN
: R2_COPYPEN
);
1558 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor
)
1560 SetLineColor( ImplGetROPColor( nROPColor
) );
1563 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor
)
1565 SetFillColor( ImplGetROPColor( nROPColor
) );
1568 void WinSalGraphicsImpl::DrawPixelImpl( tools::Long nX
, tools::Long nY
, COLORREF crColor
)
1570 const HDC hDC
= mrParent
.getHDC();
1574 SetPixel(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), crColor
);
1578 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(crColor
));
1579 PatBlt(hDC
, static_cast<int>(nX
), static_cast<int>(nY
), int(1), int(1), PATINVERT
);
1582 void WinSalGraphicsImpl::drawPixel( tools::Long nX
, tools::Long nY
)
1584 DrawPixelImpl( nX
, nY
, mnPenColor
);
1587 void WinSalGraphicsImpl::drawPixel( tools::Long nX
, tools::Long nY
, Color nColor
)
1589 COLORREF nCol
= PALETTERGB( nColor
.GetRed(),
1593 if ( !mrParent
.isPrinter() &&
1594 GetSalData()->mhDitherPal
&&
1595 ImplIsSysColorEntry( nColor
) )
1596 nCol
= PALRGB_TO_RGB( nCol
);
1598 DrawPixelImpl( nX
, nY
, nCol
);
1601 void WinSalGraphicsImpl::drawLine( tools::Long nX1
, tools::Long nY1
, tools::Long nX2
, tools::Long nY2
)
1603 MoveToEx( mrParent
.getHDC(), static_cast<int>(nX1
), static_cast<int>(nY1
), nullptr );
1605 LineTo( mrParent
.getHDC(), static_cast<int>(nX2
), static_cast<int>(nY2
) );
1607 // LineTo doesn't draw the last pixel
1608 if ( !mrParent
.isPrinter() )
1609 DrawPixelImpl( nX2
, nY2
, mnPenColor
);
1612 void WinSalGraphicsImpl::drawRect( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
)
1616 if ( !mrParent
.isPrinter() )
1618 PatBlt( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nWidth
), static_cast<int>(nHeight
),
1619 mbXORMode
? PATINVERT
: PATCOPY
);
1626 aWinRect
.right
= nX
+nWidth
;
1627 aWinRect
.bottom
= nY
+nHeight
;
1628 ::FillRect( mrParent
.getHDC(), &aWinRect
, mhBrush
);
1632 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
1635 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints
, const Point
* pPtAry
)
1637 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1638 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1639 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1641 // for Windows 95 and its maximum number of points
1642 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1643 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1645 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1646 if ( !mrParent
.isPrinter() )
1647 DrawPixelImpl( pWinPtAry
[nPoints
-1].x
, pWinPtAry
[nPoints
-1].y
, mnPenColor
);
1650 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints
, const Point
* pPtAry
)
1652 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1653 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1654 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1656 // for Windows 95 and its maximum number of points
1657 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1658 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1661 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1662 const Point
** pPtAry
)
1664 UINT aWinPointAry
[SAL_POLYPOLYCOUNT_STACKBUF
];
1666 UINT nPolyPolyPoints
= 0;
1670 if ( nPoly
<= SAL_POLYPOLYCOUNT_STACKBUF
)
1671 pWinPointAry
= aWinPointAry
;
1673 pWinPointAry
= new UINT
[nPoly
];
1675 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1677 nPoints
= static_cast<UINT
>(pPoints
[i
])+1;
1678 pWinPointAry
[i
] = nPoints
;
1679 nPolyPolyPoints
+= nPoints
;
1682 POINT aWinPointAryAry
[SAL_POLYPOLYPOINTS_STACKBUF
];
1683 POINT
* pWinPointAryAry
;
1684 if ( nPolyPolyPoints
<= SAL_POLYPOLYPOINTS_STACKBUF
)
1685 pWinPointAryAry
= aWinPointAryAry
;
1687 pWinPointAryAry
= new POINT
[nPolyPolyPoints
];
1689 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1691 nPoints
= pWinPointAry
[i
];
1692 const Point
* pPolyAry
= pPtAry
[i
];
1693 for (sal_uInt32 j
=0; j
<nPoints
-1; ++j
)
1694 pWinPointAryAry
[n
+j
] = POINT
{ static_cast<LONG
>(pPolyAry
[j
].getX()), static_cast<LONG
>(pPolyAry
[j
].getY()) };
1695 pWinPointAryAry
[n
+nPoints
-1] = pWinPointAryAry
[n
];
1699 if ( !PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), static_cast<UINT
>(nPoly
) ) &&
1700 (nPolyPolyPoints
> MAX_64KSALPOINTS
) )
1702 nPolyPolyPoints
= 0;
1706 nPolyPolyPoints
+= pWinPointAry
[static_cast<UINT
>(nPoly
)];
1709 while ( nPolyPolyPoints
< MAX_64KSALPOINTS
);
1711 if ( pWinPointAry
[static_cast<UINT
>(nPoly
)] > MAX_64KSALPOINTS
)
1712 pWinPointAry
[static_cast<UINT
>(nPoly
)] = MAX_64KSALPOINTS
;
1714 Polygon( mrParent
.getHDC(), pWinPointAryAry
, *pWinPointAry
);
1716 PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), nPoly
);
1719 if ( pWinPointAry
!= aWinPointAry
)
1720 delete [] pWinPointAry
;
1721 if ( pWinPointAryAry
!= aWinPointAryAry
)
1722 delete [] pWinPointAryAry
;
1725 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1727 // #100127# draw an array of points which might also contain bezier control points
1731 const HDC hdc
= mrParent
.getHDC();
1733 // TODO: profile whether the following options are faster:
1734 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1735 // b) convert our flag array to window's and use PolyDraw
1736 MoveToEx(hdc
, static_cast<LONG
>(pPtAry
->getX()), static_cast<LONG
>(pPtAry
->getY()), nullptr);
1740 for(sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1742 if(*pFlgAry
!= PolyFlags::Control
)
1744 LineTo(hdc
, pPtAry
->getX(), pPtAry
->getY());
1746 else if(nPoints
- i
> 2)
1748 POINT bezierPoints
[] = {
1749 POINT
{ static_cast<LONG
>(pPtAry
[0].getX()), static_cast<LONG
>(pPtAry
[0].getY()) },
1750 POINT
{ static_cast<LONG
>(pPtAry
[1].getX()), static_cast<LONG
>(pPtAry
[1].getY()) },
1751 POINT
{ static_cast<LONG
>(pPtAry
[2].getX()), static_cast<LONG
>(pPtAry
[2].getY()) },
1753 PolyBezierTo(hdc
, bezierPoints
, 3);
1766 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1768 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1769 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1770 POINT
* pWinPointAry
;
1772 if( nPoints
> SAL_POLY_STACKBUF
)
1774 pWinPointAry
= new POINT
[ nPoints
];
1775 pWinFlagAry
= new BYTE
[ nPoints
];
1779 pWinPointAry
= aStackAry1
;
1780 pWinFlagAry
= aStackAry2
;
1783 sal_uInt32
nPoints_i32(nPoints
);
1784 ImplPreparePolyDraw(true, 1, &nPoints_i32
, &pPtAry
, &pFlgAry
, pWinPointAry
, pWinFlagAry
);
1788 if( BeginPath( mrParent
.getHDC() ) )
1790 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nPoints
);
1792 if( EndPath( mrParent
.getHDC() ) )
1794 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1799 if( pWinPointAry
!= aStackAry1
)
1801 delete [] pWinPointAry
;
1802 delete [] pWinFlagAry
;
1808 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1809 const Point
* const* pPtAry
, const PolyFlags
* const* pFlgAry
)
1811 sal_uLong nCurrPoly
, nTotalPoints
;
1812 const sal_uInt32
* pCurrPoints
= pPoints
;
1813 for( nCurrPoly
=0, nTotalPoints
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
1814 nTotalPoints
+= *pCurrPoints
++;
1816 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1817 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1818 POINT
* pWinPointAry
;
1820 if( nTotalPoints
> SAL_POLY_STACKBUF
)
1822 pWinPointAry
= new POINT
[ nTotalPoints
];
1823 pWinFlagAry
= new BYTE
[ nTotalPoints
];
1827 pWinPointAry
= aStackAry1
;
1828 pWinFlagAry
= aStackAry2
;
1831 ImplPreparePolyDraw(true, nPoly
, pPoints
, pPtAry
, pFlgAry
, pWinPointAry
, pWinFlagAry
);
1835 if( BeginPath( mrParent
.getHDC() ) )
1837 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nTotalPoints
);
1839 if( EndPath( mrParent
.getHDC() ) )
1841 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1846 if( pWinPointAry
!= aStackAry1
)
1848 delete [] pWinPointAry
;
1849 delete [] pWinFlagAry
;
1855 static basegfx::B2DPoint
impPixelSnap(
1856 const basegfx::B2DPolygon
& rPolygon
,
1857 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1858 basegfx::B2DHomMatrix
& rObjectToDeviceInv
,
1861 const sal_uInt32
nCount(rPolygon
.count());
1864 const basegfx::B2ITuple
aPrevTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ nCount
- 1) % nCount
)));
1865 const basegfx::B2DPoint
aCurrPoint(rObjectToDevice
* rPolygon
.getB2DPoint(nIndex
));
1866 const basegfx::B2ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1867 const basegfx::B2ITuple
aNextTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ 1) % nCount
)));
1870 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1871 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1872 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1873 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1874 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1875 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1877 if(bSnapX
|| bSnapY
)
1879 basegfx::B2DPoint
aSnappedPoint(
1880 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1881 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY());
1883 if(rObjectToDeviceInv
.isIdentity())
1885 rObjectToDeviceInv
= rObjectToDevice
;
1886 rObjectToDeviceInv
.invert();
1889 aSnappedPoint
*= rObjectToDeviceInv
;
1891 return aSnappedPoint
;
1894 return rPolygon
.getB2DPoint(nIndex
);
1897 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1898 Gdiplus::GraphicsPath
& rGraphicsPath
,
1899 const basegfx::B2DPolygon
& rPolygon
,
1900 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1902 bool bPixelSnapHairline
)
1904 sal_uInt32
nCount(rPolygon
.count());
1908 const sal_uInt32
nEdgeCount(rPolygon
.isClosed() ? nCount
: nCount
- 1);
1912 const bool bControls(rPolygon
.areControlPointsUsed());
1913 basegfx::B2DPoint
aCurr(rPolygon
.getB2DPoint(0));
1914 basegfx::B2DHomMatrix aObjectToDeviceInv
;
1916 if(bPixelSnapHairline
)
1918 aCurr
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, 0);
1921 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1923 const sal_uInt32
nNextIndex((a
+ 1) % nCount
);
1924 basegfx::B2DPoint
aNext(rPolygon
.getB2DPoint(nNextIndex
));
1925 const bool b1stControlPointUsed(bControls
&& rPolygon
.isNextControlPointUsed(a
));
1926 const bool b2ndControlPointUsed(bControls
&& rPolygon
.isPrevControlPointUsed(nNextIndex
));
1928 if(bPixelSnapHairline
)
1930 aNext
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, nNextIndex
);
1933 if(b1stControlPointUsed
|| b2ndControlPointUsed
)
1935 basegfx::B2DPoint
aCa(rPolygon
.getNextControlPoint(a
));
1936 basegfx::B2DPoint
aCb(rPolygon
.getPrevControlPoint(nNextIndex
));
1938 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1939 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1940 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1941 // (basegfx can handle that).
1942 // Caution: This error (and it's correction) might be necessary for other graphical
1943 // sub-systems in a similar way.
1944 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1945 // vector was wrong. Best alternative is one as close as possible which means short.
1946 if(!b1stControlPointUsed
)
1948 aCa
= aCurr
+ ((aCb
- aCurr
) * 0.0005);
1950 else if(!b2ndControlPointUsed
)
1952 aCb
= aNext
+ ((aCa
- aNext
) * 0.0005);
1955 rGraphicsPath
.AddBezier(
1956 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1957 static_cast< Gdiplus::REAL
>(aCa
.getX()), static_cast< Gdiplus::REAL
>(aCa
.getY()),
1958 static_cast< Gdiplus::REAL
>(aCb
.getX()), static_cast< Gdiplus::REAL
>(aCb
.getY()),
1959 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1963 rGraphicsPath
.AddLine(
1964 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1965 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1968 if(a
+ 1 < nEdgeCount
)
1974 rGraphicsPath
.StartFigure();
1984 class SystemDependentData_GraphicsPath
: public basegfx::SystemDependentData
1987 // the path data itself
1988 std::shared_ptr
<Gdiplus::GraphicsPath
> mpGraphicsPath
;
1990 // all other values the triangulation is based on and
1991 // need to be compared with to check for data validity
1993 std::vector
< double > maStroke
;
1996 SystemDependentData_GraphicsPath(
1997 basegfx::SystemDependentDataManager
& rSystemDependentDataManager
,
1998 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
2000 const std::vector
< double >* pStroke
); // MM01
2003 std::shared_ptr
<Gdiplus::GraphicsPath
>& getGraphicsPath() { return mpGraphicsPath
; }
2004 bool getNoLineJoin() const { return mbNoLineJoin
; }
2005 const std::vector
< double >& getStroke() const { return maStroke
; }
2007 virtual sal_Int64
estimateUsageInBytes() const override
;
2012 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
2013 basegfx::SystemDependentDataManager
& rSystemDependentDataManager
,
2014 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
2016 const std::vector
< double >* pStroke
)
2017 : basegfx::SystemDependentData(rSystemDependentDataManager
),
2018 mpGraphicsPath(rpGraphicsPath
),
2019 mbNoLineJoin(bNoLineJoin
),
2022 if(nullptr != pStroke
)
2024 maStroke
= *pStroke
;
2028 sal_Int64
SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2030 sal_Int64
nRetval(0);
2034 const INT
nPointCount(mpGraphicsPath
->GetPointCount());
2036 if(0 != nPointCount
)
2039 // - 2 x sizeof(Gdiplus::REAL)
2040 // - 1 byte (see GetPathTypes in docu)
2041 nRetval
= nPointCount
* ((2 * sizeof(Gdiplus::REAL
)) + 1);
2048 bool WinSalGraphicsImpl::drawPolyPolygon(
2049 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2050 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
2051 double fTransparency
)
2053 const sal_uInt32
nCount(rPolyPolygon
.count());
2055 if(!mbBrush
|| 0 == nCount
|| fTransparency
< 0.0 || fTransparency
> 1.0)
2060 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2061 const sal_uInt8
aTrans(sal_uInt8(255) - static_cast<sal_uInt8
>(basegfx::fround(fTransparency
* 255.0)));
2062 const Gdiplus::Color
aTestColor(aTrans
, maFillColor
.GetRed(), maFillColor
.GetGreen(), maFillColor
.GetBlue());
2063 const Gdiplus::SolidBrush
aSolidBrush(aTestColor
.GetValue());
2065 // Set full (Object-to-Device) transformation - if used
2066 if(rObjectToDevice
.isIdentity())
2068 aGraphics
.ResetTransform();
2072 Gdiplus::Matrix aMatrix
;
2074 aMatrix
.SetElements(
2075 rObjectToDevice
.get(0, 0),
2076 rObjectToDevice
.get(1, 0),
2077 rObjectToDevice
.get(0, 1),
2078 rObjectToDevice
.get(1, 1),
2079 rObjectToDevice
.get(0, 2),
2080 rObjectToDevice
.get(1, 2));
2081 aGraphics
.SetTransform(&aMatrix
);
2084 // prepare local instance of Gdiplus::GraphicsPath
2085 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2087 // try to access buffered data
2088 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2089 rPolyPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2091 if(pSystemDependentData_GraphicsPath
)
2093 // copy buffered data
2094 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2098 // Note: In principle we could use the same buffered geometry at line
2099 // and fill polygons. Checked that in a first try, used
2100 // GraphicsPath::AddPath from Gdiplus combined with below used
2101 // StartFigure/CloseFigure, worked well (thus the line-draw version
2102 // may create non-closed partial Polygon data).
2104 // But in current reality it gets not used due to e.g.
2105 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2106 // line and fill polygon-primitives (what could be changed).
2108 // There will probably be more hindrances here in other rendering paths
2109 // which could all be found - intention to do this would be: Use more
2110 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2112 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2113 // and embed into a TransformPrimitive2D containing the transformation.
2115 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2116 // && !bIsHairline) creates polygon fill infos that are not reusable
2117 // for the fill case (see ::drawPolyLine below) - thus we would need a
2118 // bool and/or two system-dependent paths buffered - doable, but complicated.
2120 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2121 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2122 // (at least for now...)
2125 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2127 for(sal_uInt32
a(0); a
< nCount
; a
++)
2131 // #i101491# not needed for first run
2132 pGraphicsPath
->StartFigure();
2135 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2137 rPolyPolygon
.getB2DPolygon(a
),
2138 rObjectToDevice
, // not used due to the two 'false' values below, but to not forget later
2142 pGraphicsPath
->CloseFigure();
2145 // add to buffering mechanism
2146 rPolyPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2147 ImplGetSystemDependentDataManager(),
2153 if(mrParent
.getAntiAlias())
2155 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2159 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2162 if(mrParent
.isPrinter())
2165 // Normally GdiPlus should not be used for printing at all since printers cannot
2166 // print transparent filled polygon geometry and normally this does not happen
2167 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2168 // and no transparent parts should remain for printing. But this can be overridden
2169 // by the user and thus happens. This call can only come (currently) from
2170 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2171 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2172 // checked that there is *no* transformation set and estimated that a stable factor
2173 // dependent of the printer's DPI is used. Create and set a transformation here to
2175 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2176 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2178 // Now the transformation maybe/is already used (see above), so do
2179 // modify it without resetting to not destroy it.
2180 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2181 // we need - in our notation, would be a multiply from left to execute
2182 // current transform first and this scale last.
2183 // I tried to trigger this code using Print from the menu and various
2184 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2185 // more, feel free to remove it.
2186 // One more hint: This *may* also be needed now in ::drawPolyLine below
2187 // since it also uses transformations now.
2189 // aGraphics.ResetTransform();
2191 aGraphics
.ScaleTransform(
2192 Gdiplus::REAL(100.0) / aDpiX
,
2193 Gdiplus::REAL(100.0) / aDpiY
,
2194 Gdiplus::MatrixOrderAppend
);
2197 // use created or buffered data
2205 bool WinSalGraphicsImpl::drawPolyLine(
2206 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2207 const basegfx::B2DPolygon
& rPolygon
,
2208 double fTransparency
,
2210 const std::vector
< double >* pStroke
, // MM01
2211 basegfx::B2DLineJoin eLineJoin
,
2212 css::drawing::LineCap eLineCap
,
2213 double fMiterMinimumAngle
,
2214 bool bPixelSnapHairline
)
2216 // MM01 check done for simple reasons
2217 if(!mbPen
|| !rPolygon
.count() || fTransparency
< 0.0 || fTransparency
> 1.0)
2222 // need to check/handle LineWidth when ObjectToDevice transformation is used
2223 const bool bObjectToDeviceIsIdentity(rObjectToDevice
.isIdentity());
2224 const bool bIsHairline(fLineWidth
== 0);
2226 // tdf#124848 calculate-back logical LineWidth for a hairline
2227 // since this implementation hands over the transformation to
2228 // the graphic sub-system
2233 if(!bObjectToDeviceIsIdentity
)
2235 basegfx::B2DHomMatrix
aObjectToDeviceInv(rObjectToDevice
);
2236 aObjectToDeviceInv
.invert();
2237 fLineWidth
= (aObjectToDeviceInv
* basegfx::B2DVector(fLineWidth
, 0)).getLength();
2241 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2242 const sal_uInt8 aTrans
= static_cast<sal_uInt8
>(basegfx::fround( 255 * (1.0 - fTransparency
) ));
2243 const Gdiplus::Color
aTestColor(aTrans
, maLineColor
.GetRed(), maLineColor
.GetGreen(), maLineColor
.GetBlue());
2244 Gdiplus::Pen
aPen(aTestColor
.GetValue(), Gdiplus::REAL(fLineWidth
));
2245 bool bNoLineJoin(false);
2247 // Set full (Object-to-Device) transformation - if used
2248 if(bObjectToDeviceIsIdentity
)
2250 aGraphics
.ResetTransform();
2254 Gdiplus::Matrix aMatrix
;
2256 aMatrix
.SetElements(
2257 rObjectToDevice
.get(0, 0),
2258 rObjectToDevice
.get(1, 0),
2259 rObjectToDevice
.get(0, 1),
2260 rObjectToDevice
.get(1, 1),
2261 rObjectToDevice
.get(0, 2),
2262 rObjectToDevice
.get(1, 2));
2263 aGraphics
.SetTransform(&aMatrix
);
2268 case basegfx::B2DLineJoin::NONE
:
2276 case basegfx::B2DLineJoin::Bevel
:
2278 aPen
.SetLineJoin(Gdiplus::LineJoinBevel
);
2281 case basegfx::B2DLineJoin::Miter
:
2283 const Gdiplus::REAL
aMiterLimit(1.0/sin(fMiterMinimumAngle
/2.0));
2285 aPen
.SetMiterLimit(aMiterLimit
);
2286 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2287 // graphics, somewhere clipped in some distance from the edge point, dependent
2288 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2290 aPen
.SetLineJoin(Gdiplus::LineJoinMiterClipped
);
2293 case basegfx::B2DLineJoin::Round
:
2295 aPen
.SetLineJoin(Gdiplus::LineJoinRound
);
2302 default: /*css::drawing::LineCap_BUTT*/
2307 case css::drawing::LineCap_ROUND
:
2309 aPen
.SetStartCap(Gdiplus::LineCapRound
);
2310 aPen
.SetEndCap(Gdiplus::LineCapRound
);
2313 case css::drawing::LineCap_SQUARE
:
2315 aPen
.SetStartCap(Gdiplus::LineCapSquare
);
2316 aPen
.SetEndCap(Gdiplus::LineCapSquare
);
2321 // prepare local instance of Gdiplus::GraphicsPath
2322 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2324 // try to access buffered data
2325 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2326 rPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2328 // MM01 need to do line dashing as fallback stuff here now
2329 const double fDotDashLength(nullptr != pStroke
? std::accumulate(pStroke
->begin(), pStroke
->end(), 0.0) : 0.0);
2330 const bool bStrokeUsed(0.0 != fDotDashLength
);
2331 assert(!bStrokeUsed
|| (bStrokeUsed
&& pStroke
));
2333 // MM01 decide if to stroke directly
2334 static bool bDoDirectGDIPlusStroke(true);
2336 // activate to stroke directly
2337 if(bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2339 // tdf#124848 the fix of tdf#130478 that was needed here before
2340 // gets much easier when already handling the hairline case above,
2341 // the back-calculated logical linewidth is already here, just use it.
2342 // Still be careful - a zero LineWidth *should* not happen, but...
2343 std::vector
<Gdiplus::REAL
> aDashArray(pStroke
->size());
2344 const double fFactor(fLineWidth
== 0 ? 1.0 : 1.0 / fLineWidth
);
2346 // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
2347 // dash or dot themselve. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
2348 // and other renderes (e.g. Impress slide show), while keeping the total length of the
2350 // Patterns are always a sequence dash space dash space ...
2351 if (eLineCap
!= css::drawing::LineCap_BUTT
)
2353 size_t nSize
= pStroke
->size();
2354 // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
2357 for(size_t a(0); a
< nSize
; a
++)
2359 double fDashLengthRel
= (*pStroke
)[2 * a
] * fFactor
;
2360 double fSpaceLengthRel
= (*pStroke
)[2 * a
+ 1] * fFactor
;
2361 // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
2362 // appearance is different, in case space is too small.
2363 double fCorrect
= fSpaceLengthRel
- 1.0 <= 0 ? fSpaceLengthRel
- 0.01 : 1.0;
2364 aDashArray
[2 * a
] = Gdiplus::REAL(fDashLengthRel
+ fCorrect
);
2365 aDashArray
[2 * a
+ 1] = Gdiplus::REAL(fSpaceLengthRel
- fCorrect
);
2370 for(size_t a(0); a
< pStroke
->size(); a
++)
2372 aDashArray
[a
] = Gdiplus::REAL((*pStroke
)[a
] * fFactor
);
2375 if (eLineCap
== css::drawing::LineCap_ROUND
)
2376 aPen
.SetDashCap(Gdiplus::DashCapRound
);
2378 aPen
.SetDashCap(Gdiplus::DashCapFlat
); // "square" doesn't exist in Gdiplus
2379 aPen
.SetDashOffset(Gdiplus::REAL(0.0));
2380 aPen
.SetDashPattern(aDashArray
.data(), aDashArray
.size());
2383 if(!bDoDirectGDIPlusStroke
&& pSystemDependentData_GraphicsPath
)
2385 // MM01 - check on stroke change. Used against not used, or if oth used,
2386 // equal or different? Triangulation geometry creation depends heavily
2387 // on stroke, independent of being transformation independent
2388 const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath
->getStroke().empty());
2390 if(bStrokeWasUsed
!= bStrokeUsed
2391 || (bStrokeUsed
&& *pStroke
!= pSystemDependentData_GraphicsPath
->getStroke()))
2393 // data invalid, forget
2394 pSystemDependentData_GraphicsPath
.reset();
2398 if(pSystemDependentData_GraphicsPath
)
2400 // check data validity
2401 if (pSystemDependentData_GraphicsPath
->getNoLineJoin() != bNoLineJoin
2402 || bPixelSnapHairline
/*tdf#124700*/)
2404 // data invalid, forget
2405 pSystemDependentData_GraphicsPath
.reset();
2409 if(pSystemDependentData_GraphicsPath
)
2411 // copy buffered data
2412 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2416 // fill data of buffered data
2417 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2419 if(!bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2421 // MM01 need to do line dashing as fallback stuff here now
2422 basegfx::B2DPolyPolygon aPolyPolygonLine
;
2425 basegfx::utils::applyLineDashing(
2427 *pStroke
, // pattern
2428 &aPolyPolygonLine
, // target for lines
2429 nullptr, // target for gaps
2430 fDotDashLength
); // full length if available
2432 // MM01 checked/verified, ok
2433 for(sal_uInt32
a(0); a
< aPolyPolygonLine
.count(); a
++)
2435 const basegfx::B2DPolygon
aPolyLine(aPolyPolygonLine
.getB2DPolygon(a
));
2436 pGraphicsPath
->StartFigure();
2437 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2442 bPixelSnapHairline
);
2447 // no line dashing or direct stroke, just copy
2448 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2453 bPixelSnapHairline
);
2455 if(rPolygon
.isClosed() && !bNoLineJoin
)
2457 // #i101491# needed to create the correct line joins
2458 pGraphicsPath
->CloseFigure();
2462 // add to buffering mechanism
2463 if (!bPixelSnapHairline
/*tdf#124700*/)
2465 rPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2466 ImplGetSystemDependentDataManager(),
2473 if(mrParent
.getAntiAlias())
2475 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2479 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2482 if(mrParent
.isPrinter())
2484 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2485 // (look for 'One more hint: This *may* also be needed now in'...).
2486 // See comments in same spot above *urgently* before doing changes here,
2487 // these comments are *still fully valid* at this place (!)
2488 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2489 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2491 aGraphics
.ScaleTransform(
2492 Gdiplus::REAL(100.0) / aDpiX
,
2493 Gdiplus::REAL(100.0) / aDpiY
,
2494 Gdiplus::MatrixOrderAppend
);
2504 static void paintToGdiPlus(
2505 Gdiplus::Graphics
& rGraphics
,
2506 const SalTwoRect
& rTR
,
2507 Gdiplus::Bitmap
& rBitmap
)
2509 // only parts of source are used
2510 Gdiplus::PointF aDestPoints
[3];
2511 Gdiplus::ImageAttributes aAttributes
;
2513 // define target region as parallelogram
2514 aDestPoints
[0].X
= Gdiplus::REAL(rTR
.mnDestX
);
2515 aDestPoints
[0].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2516 aDestPoints
[1].X
= Gdiplus::REAL(rTR
.mnDestX
+ rTR
.mnDestWidth
);
2517 aDestPoints
[1].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2518 aDestPoints
[2].X
= Gdiplus::REAL(rTR
.mnDestX
);
2519 aDestPoints
[2].Y
= Gdiplus::REAL(rTR
.mnDestY
+ rTR
.mnDestHeight
);
2521 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2523 rGraphics
.DrawImage(
2527 Gdiplus::REAL(rTR
.mnSrcX
),
2528 Gdiplus::REAL(rTR
.mnSrcY
),
2529 Gdiplus::REAL(rTR
.mnSrcWidth
),
2530 Gdiplus::REAL(rTR
.mnSrcHeight
),
2535 static void setInterpolationMode(
2536 Gdiplus::Graphics
& rGraphics
,
2537 tools::Long rSrcWidth
,
2538 tools::Long rDestWidth
,
2539 tools::Long rSrcHeight
,
2540 tools::Long rDestHeight
)
2542 const bool bSameWidth(rSrcWidth
== rDestWidth
);
2543 const bool bSameHeight(rSrcHeight
== rDestHeight
);
2545 if(bSameWidth
&& bSameHeight
)
2547 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeInvalid
);
2549 else if(rDestWidth
> rSrcWidth
&& rDestHeight
> rSrcHeight
)
2551 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2553 else if(rDestWidth
< rSrcWidth
&& rDestHeight
< rSrcHeight
)
2555 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeBicubic
);
2559 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2563 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect
& rTR
, const SalBitmap
& rSrcBitmap
)
2565 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2567 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2569 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2570 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap());
2574 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2576 setInterpolationMode(
2595 bool WinSalGraphicsImpl::blendBitmap(
2602 bool WinSalGraphicsImpl::blendAlphaBitmap(
2611 bool WinSalGraphicsImpl::drawAlphaBitmap(
2612 const SalTwoRect
& rTR
,
2613 const SalBitmap
& rSrcBitmap
,
2614 const SalBitmap
& rAlphaBmp
)
2616 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2618 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2619 assert(dynamic_cast<const WinSalBitmap
*>(&rAlphaBmp
));
2621 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2622 const WinSalBitmap
& rSalAlpha
= static_cast< const WinSalBitmap
& >(rAlphaBmp
);
2623 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(&rSalAlpha
));
2627 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2629 setInterpolationMode(
2648 bool WinSalGraphicsImpl::drawTransformedBitmap(
2649 const basegfx::B2DPoint
& rNull
,
2650 const basegfx::B2DPoint
& rX
,
2651 const basegfx::B2DPoint
& rY
,
2652 const SalBitmap
& rSourceBitmap
,
2653 const SalBitmap
* pAlphaBitmap
)
2655 assert(dynamic_cast<const WinSalBitmap
*>(&rSourceBitmap
));
2656 assert(!pAlphaBitmap
|| dynamic_cast<const WinSalBitmap
*>(pAlphaBitmap
));
2658 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSourceBitmap
);
2659 const WinSalBitmap
* pSalAlpha
= static_cast< const WinSalBitmap
* >(pAlphaBitmap
);
2660 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(pSalAlpha
));
2664 const tools::Long
nSrcWidth(aARGB
->GetWidth());
2665 const tools::Long
nSrcHeight(aARGB
->GetHeight());
2667 if(nSrcWidth
&& nSrcHeight
)
2669 const tools::Long
nDestWidth(basegfx::fround(basegfx::B2DVector(rX
- rNull
).getLength()));
2670 const tools::Long
nDestHeight(basegfx::fround(basegfx::B2DVector(rY
- rNull
).getLength()));
2672 if(nDestWidth
&& nDestHeight
)
2674 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2675 Gdiplus::PointF aDestPoints
[3];
2676 Gdiplus::ImageAttributes aAttributes
;
2678 setInterpolationMode(
2685 // this mode is only capable of drawing the whole bitmap to a parallelogram
2686 aDestPoints
[0].X
= Gdiplus::REAL(rNull
.getX());
2687 aDestPoints
[0].Y
= Gdiplus::REAL(rNull
.getY());
2688 aDestPoints
[1].X
= Gdiplus::REAL(rX
.getX());
2689 aDestPoints
[1].Y
= Gdiplus::REAL(rX
.getY());
2690 aDestPoints
[2].X
= Gdiplus::REAL(rY
.getX());
2691 aDestPoints
[2].Y
= Gdiplus::REAL(rY
.getY());
2693 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2695 aGraphics
.DrawImage(
2701 Gdiplus::REAL(nSrcWidth
),
2702 Gdiplus::REAL(nSrcHeight
),
2714 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon
& /*rPolygon*/,
2715 const Gradient
& /*rGradient*/)
2720 bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon
const & /*rPolyPolygon*/,
2721 SalGradient
const & /*rGradient*/)
2726 bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType eType
) const
2728 static bool bAllowForTest(true);
2733 case OutDevSupportType::TransparentRect
:
2734 bRet
= mrParent
.mbVirDev
|| mrParent
.mbWindow
;
2736 case OutDevSupportType::B2DDraw
:
2737 bRet
= bAllowForTest
;
2745 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */