1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
28 #include "gdiimpl.hxx"
31 #include <rtl/strbuf.hxx>
32 #include <sal/log.hxx>
33 #include <tools/poly.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
37 #include <win/wincomp.hxx>
38 #include <win/saldata.hxx>
39 #include <win/salgdi.h>
40 #include <win/salbmp.h>
41 #include <win/scoped_gdi.hxx>
42 #include <vcl/BitmapAccessMode.hxx>
43 #include <vcl/BitmapBuffer.hxx>
44 #include <vcl/BitmapPalette.hxx>
45 #include <win/salframe.h>
46 #include <basegfx/matrix/b2dhommatrixtools.hxx>
47 #include <basegfx/utils/systemdependentdata.hxx>
49 #include <win/salids.hrc>
50 #include <ControlCacheKey.hxx>
55 #include <gdiplusenums.h>
56 #include <gdipluscolor.h>
60 #define SAL_POLYPOLYCOUNT_STACKBUF 8
61 #define SAL_POLYPOLYPOINTS_STACKBUF 64
63 #define SAL_POLY_STACKBUF 32
67 // #100127# Fill point and flag memory from array of points which
68 // might also contain bezier control points for the PolyDraw() GDI method
69 // Make sure pWinPointAry and pWinFlagAry are big enough
70 void ImplPreparePolyDraw( bool bCloseFigures
,
72 const sal_uInt32
* pPoints
,
73 const Point
* const* pPtAry
,
74 const PolyFlags
* const* pFlgAry
,
79 for( nCurrPoly
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
81 const Point
* pCurrPoint
= *pPtAry
++;
82 const PolyFlags
* pCurrFlag
= *pFlgAry
++;
83 const sal_uInt32 nCurrPoints
= *pPoints
++;
84 const bool bHaveFlagArray( pCurrFlag
);
90 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
92 *pWinFlagAry
++ = PT_MOVETO
;
95 for( nCurrPoint
=1; nCurrPoint
<nCurrPoints
; )
97 // #102067# Check existence of flag array
99 ( nCurrPoint
+ 2 ) < nCurrPoints
)
101 PolyFlags
P4( pCurrFlag
[ 2 ] );
103 if( ( PolyFlags::Control
== pCurrFlag
[ 0 ] ) &&
104 ( PolyFlags::Control
== pCurrFlag
[ 1 ] ) &&
105 ( PolyFlags::Normal
== P4
|| PolyFlags::Smooth
== P4
|| PolyFlags::Symmetric
== P4
) )
108 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
110 *pWinFlagAry
++ = PT_BEZIERTO
;
113 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
115 *pWinFlagAry
++ = PT_BEZIERTO
;
118 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
120 *pWinFlagAry
++ = PT_BEZIERTO
;
128 // regular line point
129 *pWinPointAry
++ = POINT
{ static_cast<LONG
>(pCurrPoint
->getX()), static_cast<LONG
>(pCurrPoint
->getY()) };
131 *pWinFlagAry
++ = PT_LINETO
;
138 pWinFlagAry
[-1] |= PT_CLOSEFIGURE
;
143 Color
ImplGetROPColor( SalROPColor nROPColor
)
146 if ( nROPColor
== SalROPColor::N0
)
147 nColor
= Color( 0, 0, 0 );
149 nColor
= Color( 255, 255, 255 );
153 bool IsDitherColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
155 constexpr sal_uInt8 DITHER_PAL_DELTA
= 51;
157 return !(nRed
% DITHER_PAL_DELTA
) &&
158 !(nGreen
% DITHER_PAL_DELTA
) &&
159 !(nBlue
% DITHER_PAL_DELTA
);
162 bool IsPaletteColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
164 static const PALETTEENTRY aImplSalSysPalEntryAry
[] =
169 { 0, 0x80, 0x80, 0 },
171 { 0x80, 0, 0x80, 0 },
172 { 0x80, 0x80, 0, 0 },
173 { 0x80, 0x80, 0x80, 0 },
174 { 0xC0, 0xC0, 0xC0, 0 },
177 { 0, 0xFF, 0xFF, 0 },
179 { 0xFF, 0, 0xFF, 0 },
180 { 0xFF, 0xFF, 0, 0 },
181 { 0xFF, 0xFF, 0xFF, 0 }
184 for (const auto& rPalEntry
: aImplSalSysPalEntryAry
)
186 if(rPalEntry
.peRed
== nRed
&&
187 rPalEntry
.peGreen
== nGreen
&&
188 rPalEntry
.peBlue
== nBlue
)
197 bool IsExtraColor(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
199 return (nRed
== 0) && (nGreen
== 184) && (nBlue
== 255);
202 bool ImplIsPaletteEntry(BYTE nRed
, BYTE nGreen
, BYTE nBlue
)
204 return IsDitherColor(nRed
, nGreen
, nBlue
) ||
205 IsPaletteColor(nRed
, nGreen
, nBlue
) ||
206 IsExtraColor(nRed
, nGreen
, nBlue
);
211 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics
& rParent
):
223 WinSalGraphicsImpl::~WinSalGraphicsImpl()
234 DeleteBrush( mhBrush
);
238 void WinSalGraphicsImpl::Init()
242 void WinSalGraphicsImpl::freeResources()
246 bool WinSalGraphicsImpl::drawEPS(tools::Long
, tools::Long
, tools::Long
, tools::Long
, void*, sal_uInt32
)
251 void WinSalGraphicsImpl::copyBits( const SalTwoRect
& rPosAry
, SalGraphics
* pSrcGraphics
)
257 hSrcDC
= static_cast<WinSalGraphics
*>(pSrcGraphics
)->getHDC();
259 hSrcDC
= mrParent
.getHDC();
266 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
267 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
269 BitBlt( mrParent
.getHDC(),
270 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
271 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
273 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
278 int nOldStretchMode
= SetStretchBltMode( mrParent
.getHDC(), STRETCH_DELETESCANS
);
279 StretchBlt( 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
),
284 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
286 SetStretchBltMode( mrParent
.getHDC(), nOldStretchMode
);
293 void MakeInvisibleArea(const RECT
& rSrcRect
,
294 int nLeft
, int nTop
, int nRight
, int nBottom
,
295 HRGN
& rhInvalidateRgn
)
297 if (!rhInvalidateRgn
)
299 rhInvalidateRgn
= CreateRectRgnIndirect(&rSrcRect
);
302 ScopedHRGN
hTempRgn(CreateRectRgn(nLeft
, nTop
, nRight
, nBottom
));
303 CombineRgn(rhInvalidateRgn
, rhInvalidateRgn
, hTempRgn
.get(), RGN_DIFF
);
306 void ImplCalcOutSideRgn( const RECT
& rSrcRect
,
307 int nLeft
, int nTop
, int nRight
, int nBottom
,
308 HRGN
& rhInvalidateRgn
)
310 // calculate area outside the visible region
311 if (rSrcRect
.left
< nLeft
)
313 MakeInvisibleArea(rSrcRect
, -31999, 0, nLeft
, 31999, rhInvalidateRgn
);
315 if (rSrcRect
.top
< nTop
)
317 MakeInvisibleArea(rSrcRect
, 0, -31999, 31999, nTop
, rhInvalidateRgn
);
319 if (rSrcRect
.right
> nRight
)
321 MakeInvisibleArea(rSrcRect
, nRight
, 0, 31999, 31999, rhInvalidateRgn
);
323 if (rSrcRect
.bottom
> nBottom
)
325 MakeInvisibleArea(rSrcRect
, 0, nBottom
, 31999, 31999, rhInvalidateRgn
);
331 void WinSalGraphicsImpl::copyArea( tools::Long nDestX
, tools::Long nDestY
,
332 tools::Long nSrcX
, tools::Long nSrcY
,
333 tools::Long nSrcWidth
, tools::Long nSrcHeight
,
334 bool bWindowInvalidate
)
336 bool bRestoreClipRgn
= false;
337 HRGN hOldClipRgn
= nullptr;
338 int nOldClipRgnType
= ERROR
;
339 HRGN hInvalidateRgn
= nullptr;
341 // do we have to invalidate also the overlapping regions?
342 if ( bWindowInvalidate
&& mrParent
.isWindow() )
344 // compute and invalidate those parts that were either off-screen or covered by other windows
345 // while performing the above BitBlt
346 // those regions then have to be invalidated as they contain useless/wrong data
354 // restrict srcRect to this window (calc intersection)
355 aSrcRect
.left
= static_cast<int>(nSrcX
);
356 aSrcRect
.top
= static_cast<int>(nSrcY
);
357 aSrcRect
.right
= aSrcRect
.left
+static_cast<int>(nSrcWidth
);
358 aSrcRect
.bottom
= aSrcRect
.top
+static_cast<int>(nSrcHeight
);
359 GetClientRect( mrParent
.gethWnd(), &aClipRect
);
360 if ( IntersectRect( &aSrcRect
, &aSrcRect
, &aClipRect
) )
362 // transform srcRect to screen coordinates
366 ClientToScreen( mrParent
.gethWnd(), &aPt
);
367 aSrcRect
.left
+= aPt
.x
;
368 aSrcRect
.top
+= aPt
.y
;
369 aSrcRect
.right
+= aPt
.x
;
370 aSrcRect
.bottom
+= aPt
.y
;
371 hInvalidateRgn
= nullptr;
373 // compute the parts that are off screen (ie invisible)
375 ImplSalGetWorkArea( nullptr, &theScreen
, nullptr ); // find the screen area taking multiple monitors into account
376 ImplCalcOutSideRgn( aSrcRect
, theScreen
.left
, theScreen
.top
, theScreen
.right
, theScreen
.bottom
, hInvalidateRgn
);
378 // calculate regions that are covered by other windows
379 HRGN hTempRgn2
= nullptr;
380 HWND hWndTopWindow
= mrParent
.gethWnd();
381 // Find the TopLevel Window, because only Windows which are in
382 // in the foreground of our TopLevel window must be considered
383 if ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
)
385 RECT aTempRect3
= aSrcRect
;
388 hWndTopWindow
= ::GetParent( hWndTopWindow
);
390 // Test if the Parent clips our window
391 GetClientRect( hWndTopWindow
, &aTempRect
);
395 ClientToScreen( hWndTopWindow
, &aPt2
);
396 aTempRect
.left
+= aPt2
.x
;
397 aTempRect
.top
+= aPt2
.y
;
398 aTempRect
.right
+= aPt2
.x
;
399 aTempRect
.bottom
+= aPt2
.y
;
400 IntersectRect( &aTempRect3
, &aTempRect3
, &aTempRect
);
402 while ( GetWindowStyle( hWndTopWindow
) & WS_CHILD
);
404 // If one or more Parents clip our window, then we must
405 // calculate the outside area
406 if ( !EqualRect( &aSrcRect
, &aTempRect3
) )
408 ImplCalcOutSideRgn( aSrcRect
,
409 aTempRect3
.left
, aTempRect3
.top
,
410 aTempRect3
.right
, aTempRect3
.bottom
,
414 // retrieve the top-most (z-order) child window
415 hWnd
= GetWindow( GetDesktopWindow(), GW_CHILD
);
418 if ( hWnd
== hWndTopWindow
)
420 if ( IsWindowVisible( hWnd
) && !IsIconic( hWnd
) )
422 GetWindowRect( hWnd
, &aTempRect
);
423 if ( IntersectRect( &aTempRect2
, &aSrcRect
, &aTempRect
) )
425 // hWnd covers part or all of aSrcRect
426 if ( !hInvalidateRgn
)
427 hInvalidateRgn
= CreateRectRgnIndirect( &aSrcRect
);
429 // get full bounding box of hWnd
430 hTempRgn
= CreateRectRgnIndirect( &aTempRect
);
432 // get region of hWnd (the window may be shaped)
434 hTempRgn2
= CreateRectRgn( 0, 0, 0, 0 );
435 int nRgnType
= GetWindowRgn( hWnd
, hTempRgn2
);
436 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
438 // convert window region to screen coordinates
439 OffsetRgn( hTempRgn2
, aTempRect
.left
, aTempRect
.top
);
440 // and intersect with the window's bounding box
441 CombineRgn( hTempRgn
, hTempRgn
, hTempRgn2
, RGN_AND
);
443 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
444 CombineRgn( hInvalidateRgn
, hInvalidateRgn
, hTempRgn
, RGN_DIFF
);
445 DeleteRegion( hTempRgn
);
448 // retrieve the next window in the z-order, i.e. the window below hwnd
449 hWnd
= GetWindow( hWnd
, GW_HWNDNEXT
);
452 DeleteRegion( hTempRgn2
);
453 if ( hInvalidateRgn
)
455 // hInvalidateRgn contains the fully visible parts of the original srcRect
456 hTempRgn
= CreateRectRgnIndirect( &aSrcRect
);
457 // subtract it from the original rect to get the occluded parts
458 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_DIFF
);
459 DeleteRegion( hTempRgn
);
461 if ( (nRgnType
!= ERROR
) && (nRgnType
!= NULLREGION
) )
463 // move the occluded parts to the destination pos
464 int nOffX
= static_cast<int>(nDestX
-nSrcX
);
465 int nOffY
= static_cast<int>(nDestY
-nSrcY
);
466 OffsetRgn( hInvalidateRgn
, nOffX
-aPt
.x
, nOffY
-aPt
.y
);
468 // by excluding hInvalidateRgn from the system's clip region
469 // we will prevent bitblt from copying useless data
470 // especially now shadows from overlapping windows will appear (#i36344)
471 hOldClipRgn
= CreateRectRgn( 0, 0, 0, 0 );
472 nOldClipRgnType
= GetClipRgn( mrParent
.getHDC(), hOldClipRgn
);
474 bRestoreClipRgn
= true; // indicate changed clipregion and force invalidate
475 ExtSelectClipRgn( mrParent
.getHDC(), hInvalidateRgn
, RGN_DIFF
);
481 BitBlt( mrParent
.getHDC(),
482 static_cast<int>(nDestX
), static_cast<int>(nDestY
),
483 static_cast<int>(nSrcWidth
), static_cast<int>(nSrcHeight
),
485 static_cast<int>(nSrcX
), static_cast<int>(nSrcY
),
488 if( bRestoreClipRgn
)
490 // restore old clip region
491 if( nOldClipRgnType
!= ERROR
)
492 SelectClipRgn( mrParent
.getHDC(), hOldClipRgn
);
493 DeleteRegion( hOldClipRgn
);
495 // invalidate regions that were not copied
496 bool bInvalidate
= true;
498 // Combine Invalidate vcl::Region with existing ClipRegion
499 HRGN hTempRgn
= CreateRectRgn( 0, 0, 0, 0 );
500 if ( GetClipRgn( mrParent
.getHDC(), hTempRgn
) == 1 )
502 int nRgnType
= CombineRgn( hInvalidateRgn
, hTempRgn
, hInvalidateRgn
, RGN_AND
);
503 if ( (nRgnType
== ERROR
) || (nRgnType
== NULLREGION
) )
506 DeleteRegion( hTempRgn
);
510 InvalidateRgn( mrParent
.gethWnd(), hInvalidateRgn
, TRUE
);
511 // here we only initiate an update if this is the MainThread,
512 // so that there is no deadlock when handling the Paint event,
513 // as the SolarMutex is already held by this Thread
514 SalData
* pSalData
= GetSalData();
515 DWORD nCurThreadId
= GetCurrentThreadId();
516 if ( pSalData
->mnAppThreadId
== nCurThreadId
)
517 UpdateWindow( mrParent
.gethWnd() );
520 DeleteRegion( hInvalidateRgn
);
527 void ImplDrawBitmap( HDC hDC
, const SalTwoRect
& rPosAry
, const WinSalBitmap
& rSalBitmap
,
528 bool bPrinter
, int nDrawMode
)
533 HBITMAP hDrawDDB
= rSalBitmap
.ImplGethDDB();
534 std::unique_ptr
<WinSalBitmap
> xTmpSalBmp
;
535 bool bPrintDDB
= ( bPrinter
&& hDrawDDB
);
539 xTmpSalBmp
.reset(new WinSalBitmap
);
540 xTmpSalBmp
->Create(rSalBitmap
, vcl::bitDepthToPixelFormat(rSalBitmap
.GetBitCount()));
541 hDrawDIB
= xTmpSalBmp
->ImplGethDIB();
544 hDrawDIB
= rSalBitmap
.ImplGethDIB();
548 PBITMAPINFO pBI
= static_cast<PBITMAPINFO
>(GlobalLock( hDrawDIB
));
549 PBYTE pBits
= reinterpret_cast<PBYTE
>(pBI
) + pBI
->bmiHeader
.biSize
+
550 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB
) * sizeof( RGBQUAD
);
551 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
554 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
555 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
556 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(pBI
->bmiHeader
.biHeight
- rPosAry
.mnSrcHeight
- rPosAry
.mnSrcY
),
557 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
558 pBits
, pBI
, DIB_RGB_COLORS
, nDrawMode
);
560 GlobalUnlock( hDrawDIB
);
561 SetStretchBltMode( hDC
, nOldStretchMode
);
563 else if( hDrawDDB
&& !bPrintDDB
)
565 ScopedCachedHDC
<CACHED_HDC_DRAW
> hBmpDC(hDrawDDB
);
567 COLORREF nOldBkColor
= RGB(0xFF,0xFF,0xFF);
568 COLORREF nOldTextColor
= RGB(0,0,0);
569 bool bMono
= ( rSalBitmap
.GetBitCount() == 1 );
573 COLORREF nBkColor
= RGB( 0xFF, 0xFF, 0xFF );
574 COLORREF nTextColor
= RGB( 0x00, 0x00, 0x00 );
575 //fdo#33455 handle 1 bit depth pngs with palette entries
576 //to set fore/back colors
577 if (BitmapBuffer
* pBitmapBuffer
= const_cast<WinSalBitmap
&>(rSalBitmap
).AcquireBuffer(BitmapAccessMode::Info
))
579 const BitmapPalette
& rPalette
= pBitmapBuffer
->maPalette
;
580 if (rPalette
.GetEntryCount() == 2)
582 Color nCol
= rPalette
[0];
583 nTextColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
585 nBkColor
= RGB( nCol
.GetRed(), nCol
.GetGreen(), nCol
.GetBlue() );
587 const_cast<WinSalBitmap
&>(rSalBitmap
).ReleaseBuffer(pBitmapBuffer
, BitmapAccessMode::Info
);
589 nOldBkColor
= SetBkColor( hDC
, nBkColor
);
590 nOldTextColor
= ::SetTextColor( hDC
, nTextColor
);
593 if ( (rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
) &&
594 (rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
) )
597 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
598 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
600 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
605 const int nOldStretchMode
= SetStretchBltMode( hDC
, STRETCH_DELETESCANS
);
608 static_cast<int>(rPosAry
.mnDestX
), static_cast<int>(rPosAry
.mnDestY
),
609 static_cast<int>(rPosAry
.mnDestWidth
), static_cast<int>(rPosAry
.mnDestHeight
),
611 static_cast<int>(rPosAry
.mnSrcX
), static_cast<int>(rPosAry
.mnSrcY
),
612 static_cast<int>(rPosAry
.mnSrcWidth
), static_cast<int>(rPosAry
.mnSrcHeight
),
615 SetStretchBltMode( hDC
, nOldStretchMode
);
620 SetBkColor( hDC
, nOldBkColor
);
621 ::SetTextColor( hDC
, nOldTextColor
);
629 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect
& rPosAry
, const SalBitmap
& rSalBitmap
)
631 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
635 // only paint direct when no scaling and no MapMode, else the
636 // more expensive conversions may be done for short-time Bitmap/BitmapEx
637 // used for buffering only
638 if(rPosAry
.mnSrcWidth
== rPosAry
.mnDestWidth
&& rPosAry
.mnSrcHeight
== rPosAry
.mnDestHeight
)
640 bTryDirectPaint
= false;
644 // try to draw using GdiPlus directly
645 if(bTryDirectPaint
&& TryDrawBitmapGDIPlus(rPosAry
, rSalBitmap
))
650 // fall back old stuff
651 assert(dynamic_cast<const WinSalBitmap
*>(&rSalBitmap
));
653 ImplDrawBitmap(mrParent
.getHDC(), rPosAry
, static_cast<const WinSalBitmap
&>(rSalBitmap
),
654 mrParent
.isPrinter(),
655 mbXORMode
? SRCINVERT
: SRCCOPY
);
658 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect
& rPosAry
,
659 const SalBitmap
& rSSalBitmap
,
660 const SalBitmap
& rSTransparentBitmap
)
662 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
663 bool bTryDirectPaint(!mrParent
.isPrinter() && !mbXORMode
);
665 // try to draw using GdiPlus directly
666 if(bTryDirectPaint
&& drawAlphaBitmap(rPosAry
, rSSalBitmap
, rSTransparentBitmap
))
671 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
672 assert(dynamic_cast<const WinSalBitmap
*>(&rSTransparentBitmap
));
674 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
675 const WinSalBitmap
& rTransparentBitmap
= static_cast<const WinSalBitmap
&>(rSTransparentBitmap
);
677 SalTwoRect aPosAry
= rPosAry
;
678 int nDstX
= static_cast<int>(aPosAry
.mnDestX
);
679 int nDstY
= static_cast<int>(aPosAry
.mnDestY
);
680 int nDstWidth
= static_cast<int>(aPosAry
.mnDestWidth
);
681 int nDstHeight
= static_cast<int>(aPosAry
.mnDestHeight
);
682 HDC hDC
= mrParent
.getHDC();
684 ScopedHBITMAP hMemBitmap
;
685 ScopedHBITMAP hMaskBitmap
;
687 if( ( nDstWidth
> CACHED_HDC_DEFEXT
) || ( nDstHeight
> CACHED_HDC_DEFEXT
) )
689 hMemBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
690 hMaskBitmap
.reset(CreateCompatibleBitmap(hDC
, nDstWidth
, nDstHeight
));
693 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(hMemBitmap
.get());
694 ScopedCachedHDC
<CACHED_HDC_2
> hMaskDC(hMaskBitmap
.get());
696 aPosAry
.mnDestX
= aPosAry
.mnDestY
= 0;
697 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hDC
, nDstX
, nDstY
, SRCCOPY
);
699 // WIN/WNT seems to have a minor problem mapping the correct color of the
700 // mask to the palette if we draw the DIB directly ==> draw DDB
701 if( ( GetBitCount() <= 8 ) && rTransparentBitmap
.ImplGethDIB() && rTransparentBitmap
.GetBitCount() == 1 )
705 if( aTmp
.Create( rTransparentBitmap
, &mrParent
) )
706 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, aTmp
, false, SRCCOPY
);
709 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rTransparentBitmap
, false, SRCCOPY
);
711 // now MemDC contains background, MaskDC the transparency mask
713 // #105055# Respect XOR mode
716 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
717 // now MaskDC contains the bitmap area with black background
718 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCINVERT
);
719 // now MemDC contains background XORed bitmap area on top
723 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCAND
);
724 // now MemDC contains background with masked-out bitmap area
725 ImplDrawBitmap( hMaskDC
.get(), aPosAry
, rSalBitmap
, false, SRCERASE
);
726 // now MaskDC contains the bitmap area with black background
727 BitBlt( hMemDC
.get(), 0, 0, nDstWidth
, nDstHeight
, hMaskDC
.get(), 0, 0, SRCPAINT
);
728 // now MemDC contains background and bitmap merged together
731 BitBlt( hDC
, nDstX
, nDstY
, nDstWidth
, nDstHeight
, hMemDC
.get(), 0, 0, SRCCOPY
);
734 bool WinSalGraphicsImpl::drawAlphaRect( tools::Long nX
, tools::Long nY
, tools::Long nWidth
,
735 tools::Long nHeight
, sal_uInt8 nTransparency
)
737 if( mbPen
|| !mbBrush
|| mbXORMode
)
738 return false; // can only perform solid fills without XOR.
740 ScopedCachedHDC
<CACHED_HDC_1
> hMemDC(nullptr);
741 SetPixel( hMemDC
.get(), int(0), int(0), mnBrushColor
);
743 BLENDFUNCTION aFunc
= {
746 sal::static_int_cast
<sal_uInt8
>(255 - 255L*nTransparency
/100),
750 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
752 bool bRet
= GdiAlphaBlend(mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
,
753 hMemDC
.get(), 0,0,1,1,
759 void WinSalGraphicsImpl::drawMask(const SalTwoRect
& rPosAry
,
760 const SalBitmap
& rSSalBitmap
,
763 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No transparency print possible!" );
765 assert(dynamic_cast<const WinSalBitmap
*>(&rSSalBitmap
));
767 const WinSalBitmap
& rSalBitmap
= static_cast<const WinSalBitmap
&>(rSSalBitmap
);
769 SalTwoRect aPosAry
= rPosAry
;
770 const HDC hDC
= mrParent
.getHDC();
772 ScopedSelectedHBRUSH
hBrush(hDC
, CreateSolidBrush(RGB(nMaskColor
.GetRed(),
773 nMaskColor
.GetGreen(),
774 nMaskColor
.GetBlue())));
776 // WIN/WNT seems to have a minor problem mapping the correct color of the
777 // mask to the palette if we draw the DIB directly ==> draw DDB
778 if( ( GetBitCount() <= 8 ) && rSalBitmap
.ImplGethDIB() && rSalBitmap
.GetBitCount() == 1 )
782 if( aTmp
.Create( rSalBitmap
, &mrParent
) )
783 ImplDrawBitmap( hDC
, aPosAry
, aTmp
, false, 0x00B8074AUL
);
786 ImplDrawBitmap( hDC
, aPosAry
, rSalBitmap
, false, 0x00B8074AUL
);
789 std::shared_ptr
<SalBitmap
> WinSalGraphicsImpl::getBitmap( tools::Long nX
, tools::Long nY
, tools::Long nDX
, tools::Long nDY
)
791 SAL_WARN_IF( mrParent
.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
793 std::shared_ptr
<WinSalBitmap
> pSalBitmap
;
795 nDX
= std::abs( nDX
);
796 nDY
= std::abs( nDY
);
798 HDC hDC
= mrParent
.getHDC();
799 HBITMAP hBmpBitmap
= CreateCompatibleBitmap( hDC
, nDX
, nDY
);
803 ScopedCachedHDC
<CACHED_HDC_1
> hBmpDC(hBmpBitmap
);
805 bRet
= BitBlt(hBmpDC
.get(), 0, 0,
806 static_cast<int>(nDX
), static_cast<int>(nDY
), hDC
,
807 static_cast<int>(nX
), static_cast<int>(nY
), SRCCOPY
) ? TRUE
: FALSE
;
812 pSalBitmap
= std::make_shared
<WinSalBitmap
>();
814 if( !pSalBitmap
->Create( hBmpBitmap
) )
821 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
822 DeleteBitmap( hBmpBitmap
);
828 Color
WinSalGraphicsImpl::getPixel( tools::Long nX
, tools::Long nY
)
830 COLORREF aWinCol
= ::GetPixel( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
) );
832 if ( CLR_INVALID
== aWinCol
)
833 return Color( 0, 0, 0 );
835 return Color( GetRValue( aWinCol
),
836 GetGValue( aWinCol
),
837 GetBValue( aWinCol
) );
843 HBRUSH
Get50PercentBrush()
845 SalData
* pSalData
= GetSalData();
846 if ( !pSalData
->mh50Brush
)
848 if ( !pSalData
->mh50Bmp
)
849 pSalData
->mh50Bmp
= ImplLoadSalBitmap( SAL_RESID_BITMAP_50
);
850 pSalData
->mh50Brush
= CreatePatternBrush( pSalData
->mh50Bmp
);
853 return pSalData
->mh50Brush
;
858 void WinSalGraphicsImpl::invert( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::Long nHeight
, SalInvert nFlags
)
860 if ( nFlags
& SalInvert::TrackFrame
)
862 HPEN hDotPen
= CreatePen( PS_DOT
, 0, 0 );
863 HPEN hOldPen
= SelectPen( mrParent
.getHDC(), hDotPen
);
864 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), GetStockBrush( NULL_BRUSH
) );
865 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
867 Rectangle( mrParent
.getHDC(), static_cast<int>(nX
), static_cast<int>(nY
), static_cast<int>(nX
+nWidth
), static_cast<int>(nY
+nHeight
) );
869 SetROP2( mrParent
.getHDC(), nOldROP
);
870 SelectPen( mrParent
.getHDC(), hOldPen
);
871 SelectBrush( mrParent
.getHDC(), hOldBrush
);
872 DeletePen( hDotPen
);
874 else if ( nFlags
& SalInvert::N50
)
876 COLORREF nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
877 HBRUSH hOldBrush
= SelectBrush( mrParent
.getHDC(), Get50PercentBrush() );
878 PatBlt( mrParent
.getHDC(), nX
, nY
, nWidth
, nHeight
, PATINVERT
);
879 ::SetTextColor( mrParent
.getHDC(), nOldTextColor
);
880 SelectBrush( mrParent
.getHDC(), hOldBrush
);
885 aRect
.left
= static_cast<int>(nX
);
886 aRect
.top
= static_cast<int>(nY
);
887 aRect
.right
= static_cast<int>(nX
)+nWidth
;
888 aRect
.bottom
= static_cast<int>(nY
)+nHeight
;
889 ::InvertRect( mrParent
.getHDC(), &aRect
);
893 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints
, const Point
* pPtAry
, SalInvert nSalFlags
)
898 HBRUSH hOldBrush
= nullptr;
899 COLORREF nOldTextColor
RGB(0,0,0);
900 int nOldROP
= SetROP2( mrParent
.getHDC(), R2_NOT
);
902 if ( nSalFlags
& SalInvert::TrackFrame
)
903 hPen
= CreatePen( PS_DOT
, 0, 0 );
907 if ( nSalFlags
& SalInvert::N50
)
908 hBrush
= Get50PercentBrush();
910 hBrush
= GetStockBrush( BLACK_BRUSH
);
912 hPen
= GetStockPen( NULL_PEN
);
913 nOldTextColor
= ::SetTextColor( mrParent
.getHDC(), 0 );
914 hOldBrush
= SelectBrush( mrParent
.getHDC(), hBrush
);
916 hOldPen
= SelectPen( mrParent
.getHDC(), hPen
);
918 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
919 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
920 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
922 // for Windows 95 and its maximum number of points
923 if ( nSalFlags
& SalInvert::TrackFrame
)
925 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
926 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
930 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
931 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), 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 tools::Long
WinSalGraphicsImpl::GetGraphicsWidth() const
953 if( mrParent
.gethWnd() && IsWindow( mrParent
.gethWnd() ) )
955 WinSalFrame
* pFrame
= GetWindowPtr( mrParent
.gethWnd() );
958 if (pFrame
->maGeometry
.width())
959 return pFrame
->maGeometry
.width();
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 tools::Long
nW(rectangle
.GetWidth());
1170 const tools::Long
nH(rectangle
.GetHeight());
1174 const tools::Long
nRight(rectangle
.Left() + nW
);
1175 const tools::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( tools::Long nX
, tools::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( tools::Long nX
, tools::Long nY
)
1574 DrawPixelImpl( nX
, nY
, mnPenColor
);
1577 void WinSalGraphicsImpl::drawPixel( tools::Long nX
, tools::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( tools::Long nX1
, tools::Long nY1
, tools::Long nX2
, tools::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( tools::Long nX
, tools::Long nY
, tools::Long nWidth
, tools::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 Point
* pPtAry
)
1627 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1628 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1629 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1631 // for Windows 95 and its maximum number of points
1632 if ( !Polyline( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1633 Polyline( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1635 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1636 if ( !mrParent
.isPrinter() )
1637 DrawPixelImpl( pWinPtAry
[nPoints
-1].x
, pWinPtAry
[nPoints
-1].y
, mnPenColor
);
1640 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints
, const Point
* pPtAry
)
1642 std::unique_ptr
<POINT
[]> pWinPtAry(new POINT
[nPoints
]);
1643 for (sal_uInt32 i
=0; i
<nPoints
; ++i
)
1644 pWinPtAry
[i
] = POINT
{ static_cast<LONG
>(pPtAry
[i
].getX()), static_cast<LONG
>(pPtAry
[i
].getY()) };
1646 // for Windows 95 and its maximum number of points
1647 if ( !Polygon( mrParent
.getHDC(), pWinPtAry
.get(), static_cast<int>(nPoints
) ) && (nPoints
> MAX_64KSALPOINTS
) )
1648 Polygon( mrParent
.getHDC(), pWinPtAry
.get(), MAX_64KSALPOINTS
);
1651 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1652 const Point
** pPtAry
)
1654 UINT aWinPointAry
[SAL_POLYPOLYCOUNT_STACKBUF
];
1656 UINT nPolyPolyPoints
= 0;
1660 if ( nPoly
<= SAL_POLYPOLYCOUNT_STACKBUF
)
1661 pWinPointAry
= aWinPointAry
;
1663 pWinPointAry
= new UINT
[nPoly
];
1665 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1667 nPoints
= static_cast<UINT
>(pPoints
[i
])+1;
1668 pWinPointAry
[i
] = nPoints
;
1669 nPolyPolyPoints
+= nPoints
;
1672 POINT aWinPointAryAry
[SAL_POLYPOLYPOINTS_STACKBUF
];
1673 POINT
* pWinPointAryAry
;
1674 if ( nPolyPolyPoints
<= SAL_POLYPOLYPOINTS_STACKBUF
)
1675 pWinPointAryAry
= aWinPointAryAry
;
1677 pWinPointAryAry
= new POINT
[nPolyPolyPoints
];
1679 for ( i
= 0; i
< static_cast<UINT
>(nPoly
); i
++ )
1681 nPoints
= pWinPointAry
[i
];
1682 const Point
* pPolyAry
= pPtAry
[i
];
1683 for (sal_uInt32 j
=0; j
<nPoints
-1; ++j
)
1684 pWinPointAryAry
[n
+j
] = POINT
{ static_cast<LONG
>(pPolyAry
[j
].getX()), static_cast<LONG
>(pPolyAry
[j
].getY()) };
1685 pWinPointAryAry
[n
+nPoints
-1] = pWinPointAryAry
[n
];
1689 if ( !PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), static_cast<UINT
>(nPoly
) ) &&
1690 (nPolyPolyPoints
> MAX_64KSALPOINTS
) )
1692 nPolyPolyPoints
= 0;
1696 nPolyPolyPoints
+= pWinPointAry
[static_cast<UINT
>(nPoly
)];
1699 while ( nPolyPolyPoints
< MAX_64KSALPOINTS
);
1701 if ( pWinPointAry
[static_cast<UINT
>(nPoly
)] > MAX_64KSALPOINTS
)
1702 pWinPointAry
[static_cast<UINT
>(nPoly
)] = MAX_64KSALPOINTS
;
1704 Polygon( mrParent
.getHDC(), pWinPointAryAry
, *pWinPointAry
);
1706 PolyPolygon( mrParent
.getHDC(), pWinPointAryAry
, reinterpret_cast<int*>(pWinPointAry
), nPoly
);
1709 if ( pWinPointAry
!= aWinPointAry
)
1710 delete [] pWinPointAry
;
1711 if ( pWinPointAryAry
!= aWinPointAryAry
)
1712 delete [] pWinPointAryAry
;
1715 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1717 // #100127# draw an array of points which might also contain bezier control points
1721 const HDC hdc
= mrParent
.getHDC();
1723 // TODO: profile whether the following options are faster:
1724 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1725 // b) convert our flag array to window's and use PolyDraw
1726 MoveToEx(hdc
, static_cast<LONG
>(pPtAry
->getX()), static_cast<LONG
>(pPtAry
->getY()), nullptr);
1730 for(sal_uInt32 i
= 1; i
< nPoints
; ++i
)
1732 if(*pFlgAry
!= PolyFlags::Control
)
1734 LineTo(hdc
, pPtAry
->getX(), pPtAry
->getY());
1736 else if(nPoints
- i
> 2)
1738 POINT bezierPoints
[] = {
1739 POINT
{ static_cast<LONG
>(pPtAry
[0].getX()), static_cast<LONG
>(pPtAry
[0].getY()) },
1740 POINT
{ static_cast<LONG
>(pPtAry
[1].getX()), static_cast<LONG
>(pPtAry
[1].getY()) },
1741 POINT
{ static_cast<LONG
>(pPtAry
[2].getX()), static_cast<LONG
>(pPtAry
[2].getY()) },
1743 PolyBezierTo(hdc
, bezierPoints
, 3);
1756 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints
, const Point
* pPtAry
, const PolyFlags
* pFlgAry
)
1758 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1759 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1760 POINT
* pWinPointAry
;
1762 if( nPoints
> SAL_POLY_STACKBUF
)
1764 pWinPointAry
= new POINT
[ nPoints
];
1765 pWinFlagAry
= new BYTE
[ nPoints
];
1769 pWinPointAry
= aStackAry1
;
1770 pWinFlagAry
= aStackAry2
;
1773 sal_uInt32
nPoints_i32(nPoints
);
1774 ImplPreparePolyDraw(true, 1, &nPoints_i32
, &pPtAry
, &pFlgAry
, pWinPointAry
, pWinFlagAry
);
1778 if( BeginPath( mrParent
.getHDC() ) )
1780 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nPoints
);
1782 if( EndPath( mrParent
.getHDC() ) )
1784 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1789 if( pWinPointAry
!= aStackAry1
)
1791 delete [] pWinPointAry
;
1792 delete [] pWinFlagAry
;
1798 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly
, const sal_uInt32
* pPoints
,
1799 const Point
* const* pPtAry
, const PolyFlags
* const* pFlgAry
)
1801 sal_uLong nCurrPoly
, nTotalPoints
;
1802 const sal_uInt32
* pCurrPoints
= pPoints
;
1803 for( nCurrPoly
=0, nTotalPoints
=0; nCurrPoly
<nPoly
; ++nCurrPoly
)
1804 nTotalPoints
+= *pCurrPoints
++;
1806 POINT aStackAry1
[SAL_POLY_STACKBUF
];
1807 BYTE aStackAry2
[SAL_POLY_STACKBUF
];
1808 POINT
* pWinPointAry
;
1810 if( nTotalPoints
> SAL_POLY_STACKBUF
)
1812 pWinPointAry
= new POINT
[ nTotalPoints
];
1813 pWinFlagAry
= new BYTE
[ nTotalPoints
];
1817 pWinPointAry
= aStackAry1
;
1818 pWinFlagAry
= aStackAry2
;
1821 ImplPreparePolyDraw(true, nPoly
, pPoints
, pPtAry
, pFlgAry
, pWinPointAry
, pWinFlagAry
);
1825 if( BeginPath( mrParent
.getHDC() ) )
1827 PolyDraw(mrParent
.getHDC(), pWinPointAry
, pWinFlagAry
, nTotalPoints
);
1829 if( EndPath( mrParent
.getHDC() ) )
1831 if( StrokeAndFillPath( mrParent
.getHDC() ) )
1836 if( pWinPointAry
!= aStackAry1
)
1838 delete [] pWinPointAry
;
1839 delete [] pWinFlagAry
;
1845 static basegfx::B2DPoint
impPixelSnap(
1846 const basegfx::B2DPolygon
& rPolygon
,
1847 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1848 basegfx::B2DHomMatrix
& rObjectToDeviceInv
,
1851 const sal_uInt32
nCount(rPolygon
.count());
1854 const basegfx::B2ITuple
aPrevTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ nCount
- 1) % nCount
)));
1855 const basegfx::B2DPoint
aCurrPoint(rObjectToDevice
* rPolygon
.getB2DPoint(nIndex
));
1856 const basegfx::B2ITuple
aCurrTuple(basegfx::fround(aCurrPoint
));
1857 const basegfx::B2ITuple
aNextTuple(basegfx::fround(rObjectToDevice
* rPolygon
.getB2DPoint((nIndex
+ 1) % nCount
)));
1860 const bool bPrevVertical(aPrevTuple
.getX() == aCurrTuple
.getX());
1861 const bool bNextVertical(aNextTuple
.getX() == aCurrTuple
.getX());
1862 const bool bPrevHorizontal(aPrevTuple
.getY() == aCurrTuple
.getY());
1863 const bool bNextHorizontal(aNextTuple
.getY() == aCurrTuple
.getY());
1864 const bool bSnapX(bPrevVertical
|| bNextVertical
);
1865 const bool bSnapY(bPrevHorizontal
|| bNextHorizontal
);
1867 if(bSnapX
|| bSnapY
)
1869 basegfx::B2DPoint
aSnappedPoint(
1870 bSnapX
? aCurrTuple
.getX() : aCurrPoint
.getX(),
1871 bSnapY
? aCurrTuple
.getY() : aCurrPoint
.getY());
1873 if(rObjectToDeviceInv
.isIdentity())
1875 rObjectToDeviceInv
= rObjectToDevice
;
1876 rObjectToDeviceInv
.invert();
1879 aSnappedPoint
*= rObjectToDeviceInv
;
1881 return aSnappedPoint
;
1884 return rPolygon
.getB2DPoint(nIndex
);
1887 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1888 Gdiplus::GraphicsPath
& rGraphicsPath
,
1889 const basegfx::B2DPolygon
& rPolygon
,
1890 const basegfx::B2DHomMatrix
& rObjectToDevice
,
1892 bool bPixelSnapHairline
)
1894 sal_uInt32
nCount(rPolygon
.count());
1898 const sal_uInt32
nEdgeCount(rPolygon
.isClosed() ? nCount
: nCount
- 1);
1902 const bool bControls(rPolygon
.areControlPointsUsed());
1903 basegfx::B2DPoint
aCurr(rPolygon
.getB2DPoint(0));
1904 basegfx::B2DHomMatrix aObjectToDeviceInv
;
1906 if(bPixelSnapHairline
)
1908 aCurr
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, 0);
1911 for(sal_uInt32
a(0); a
< nEdgeCount
; a
++)
1913 const sal_uInt32
nNextIndex((a
+ 1) % nCount
);
1914 basegfx::B2DPoint
aNext(rPolygon
.getB2DPoint(nNextIndex
));
1915 const bool b1stControlPointUsed(bControls
&& rPolygon
.isNextControlPointUsed(a
));
1916 const bool b2ndControlPointUsed(bControls
&& rPolygon
.isPrevControlPointUsed(nNextIndex
));
1918 if(bPixelSnapHairline
)
1920 aNext
= impPixelSnap(rPolygon
, rObjectToDevice
, aObjectToDeviceInv
, nNextIndex
);
1923 if(b1stControlPointUsed
|| b2ndControlPointUsed
)
1925 basegfx::B2DPoint
aCa(rPolygon
.getNextControlPoint(a
));
1926 basegfx::B2DPoint
aCb(rPolygon
.getPrevControlPoint(nNextIndex
));
1928 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1929 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1930 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1931 // (basegfx can handle that).
1932 // Caution: This error (and it's correction) might be necessary for other graphical
1933 // sub-systems in a similar way.
1934 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1935 // vector was wrong. Best alternative is one as close as possible which means short.
1936 if(!b1stControlPointUsed
)
1938 aCa
= aCurr
+ ((aCb
- aCurr
) * 0.0005);
1940 else if(!b2ndControlPointUsed
)
1942 aCb
= aNext
+ ((aCa
- aNext
) * 0.0005);
1945 rGraphicsPath
.AddBezier(
1946 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1947 static_cast< Gdiplus::REAL
>(aCa
.getX()), static_cast< Gdiplus::REAL
>(aCa
.getY()),
1948 static_cast< Gdiplus::REAL
>(aCb
.getX()), static_cast< Gdiplus::REAL
>(aCb
.getY()),
1949 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1953 rGraphicsPath
.AddLine(
1954 static_cast< Gdiplus::REAL
>(aCurr
.getX()), static_cast< Gdiplus::REAL
>(aCurr
.getY()),
1955 static_cast< Gdiplus::REAL
>(aNext
.getX()), static_cast< Gdiplus::REAL
>(aNext
.getY()));
1958 if(a
+ 1 < nEdgeCount
)
1964 rGraphicsPath
.StartFigure();
1974 class SystemDependentData_GraphicsPath
: public basegfx::SystemDependentData
1977 // the path data itself
1978 std::shared_ptr
<Gdiplus::GraphicsPath
> mpGraphicsPath
;
1980 // all other values the triangulation is based on and
1981 // need to be compared with to check for data validity
1983 std::vector
< double > maStroke
;
1986 SystemDependentData_GraphicsPath(
1987 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
1989 const std::vector
< double >* pStroke
); // MM01
1992 std::shared_ptr
<Gdiplus::GraphicsPath
>& getGraphicsPath() { return mpGraphicsPath
; }
1993 bool getNoLineJoin() const { return mbNoLineJoin
; }
1994 const std::vector
< double >& getStroke() const { return maStroke
; }
1996 virtual sal_Int64
estimateUsageInBytes() const override
;
2001 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
2002 std::shared_ptr
<Gdiplus::GraphicsPath
>& rpGraphicsPath
,
2004 const std::vector
< double >* pStroke
)
2005 : basegfx::SystemDependentData(Application::GetSystemDependentDataManager()),
2006 mpGraphicsPath(rpGraphicsPath
),
2007 mbNoLineJoin(bNoLineJoin
),
2010 if(nullptr != pStroke
)
2012 maStroke
= *pStroke
;
2016 sal_Int64
SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2018 sal_Int64
nRetval(0);
2022 const INT
nPointCount(mpGraphicsPath
->GetPointCount());
2024 if(0 != nPointCount
)
2027 // - 2 x sizeof(Gdiplus::REAL)
2028 // - 1 byte (see GetPathTypes in docu)
2029 nRetval
= nPointCount
* ((2 * sizeof(Gdiplus::REAL
)) + 1);
2036 bool WinSalGraphicsImpl::drawPolyPolygon(
2037 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2038 const basegfx::B2DPolyPolygon
& rPolyPolygon
,
2039 double fTransparency
)
2041 const sal_uInt32
nCount(rPolyPolygon
.count());
2043 if(!mbBrush
|| 0 == nCount
|| fTransparency
< 0.0 || fTransparency
> 1.0)
2048 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2049 const sal_uInt8
aTrans(sal_uInt8(255) - static_cast<sal_uInt8
>(basegfx::fround(fTransparency
* 255.0)));
2050 const Gdiplus::Color
aTestColor(aTrans
, maFillColor
.GetRed(), maFillColor
.GetGreen(), maFillColor
.GetBlue());
2051 const Gdiplus::SolidBrush
aSolidBrush(aTestColor
.GetValue());
2053 // Set full (Object-to-Device) transformation - if used
2054 if(rObjectToDevice
.isIdentity())
2056 aGraphics
.ResetTransform();
2060 Gdiplus::Matrix aMatrix
;
2062 aMatrix
.SetElements(
2063 rObjectToDevice
.get(0, 0),
2064 rObjectToDevice
.get(1, 0),
2065 rObjectToDevice
.get(0, 1),
2066 rObjectToDevice
.get(1, 1),
2067 rObjectToDevice
.get(0, 2),
2068 rObjectToDevice
.get(1, 2));
2069 aGraphics
.SetTransform(&aMatrix
);
2072 // prepare local instance of Gdiplus::GraphicsPath
2073 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2075 // try to access buffered data
2076 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2077 rPolyPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2079 if(pSystemDependentData_GraphicsPath
)
2081 // copy buffered data
2082 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2086 // Note: In principle we could use the same buffered geometry at line
2087 // and fill polygons. Checked that in a first try, used
2088 // GraphicsPath::AddPath from Gdiplus combined with below used
2089 // StartFigure/CloseFigure, worked well (thus the line-draw version
2090 // may create non-closed partial Polygon data).
2092 // But in current reality it gets not used due to e.g.
2093 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2094 // line and fill polygon-primitives (what could be changed).
2096 // There will probably be more hindrances here in other rendering paths
2097 // which could all be found - intention to do this would be: Use more
2098 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2100 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2101 // and embed into a TransformPrimitive2D containing the transformation.
2103 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2104 // && !bIsHairline) creates polygon fill infos that are not reusable
2105 // for the fill case (see ::drawPolyLine below) - thus we would need a
2106 // bool and/or two system-dependent paths buffered - doable, but complicated.
2108 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2109 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2110 // (at least for now...)
2113 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2115 for(sal_uInt32
a(0); a
< nCount
; a
++)
2119 // #i101491# not needed for first run
2120 pGraphicsPath
->StartFigure();
2123 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2125 rPolyPolygon
.getB2DPolygon(a
),
2126 rObjectToDevice
, // not used due to the two 'false' values below, but to not forget later
2130 pGraphicsPath
->CloseFigure();
2133 // add to buffering mechanism
2134 rPolyPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2140 if(mrParent
.getAntiAlias())
2142 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2146 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2149 if(mrParent
.isPrinter())
2152 // Normally GdiPlus should not be used for printing at all since printers cannot
2153 // print transparent filled polygon geometry and normally this does not happen
2154 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2155 // and no transparent parts should remain for printing. But this can be overridden
2156 // by the user and thus happens. This call can only come (currently) from
2157 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2158 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2159 // checked that there is *no* transformation set and estimated that a stable factor
2160 // dependent of the printer's DPI is used. Create and set a transformation here to
2162 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2163 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2165 // Now the transformation maybe/is already used (see above), so do
2166 // modify it without resetting to not destroy it.
2167 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2168 // we need - in our notation, would be a multiply from left to execute
2169 // current transform first and this scale last.
2170 // I tried to trigger this code using Print from the menu and various
2171 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2172 // more, feel free to remove it.
2173 // One more hint: This *may* also be needed now in ::drawPolyLine below
2174 // since it also uses transformations now.
2176 // aGraphics.ResetTransform();
2178 aGraphics
.ScaleTransform(
2179 Gdiplus::REAL(100.0) / aDpiX
,
2180 Gdiplus::REAL(100.0) / aDpiY
,
2181 Gdiplus::MatrixOrderAppend
);
2184 // use created or buffered data
2192 bool WinSalGraphicsImpl::drawPolyLine(
2193 const basegfx::B2DHomMatrix
& rObjectToDevice
,
2194 const basegfx::B2DPolygon
& rPolygon
,
2195 double fTransparency
,
2197 const std::vector
< double >* pStroke
, // MM01
2198 basegfx::B2DLineJoin eLineJoin
,
2199 css::drawing::LineCap eLineCap
,
2200 double fMiterMinimumAngle
,
2201 bool bPixelSnapHairline
)
2203 // MM01 check done for simple reasons
2204 if(!mbPen
|| !rPolygon
.count() || fTransparency
< 0.0 || fTransparency
> 1.0)
2209 // need to check/handle LineWidth when ObjectToDevice transformation is used
2210 const bool bObjectToDeviceIsIdentity(rObjectToDevice
.isIdentity());
2211 const bool bIsHairline(fLineWidth
== 0);
2213 // tdf#124848 calculate-back logical LineWidth for a hairline
2214 // since this implementation hands over the transformation to
2215 // the graphic sub-system
2220 if(!bObjectToDeviceIsIdentity
)
2222 basegfx::B2DHomMatrix
aObjectToDeviceInv(rObjectToDevice
);
2223 aObjectToDeviceInv
.invert();
2224 fLineWidth
= (aObjectToDeviceInv
* basegfx::B2DVector(fLineWidth
, 0)).getLength();
2228 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2229 const sal_uInt8 aTrans
= static_cast<sal_uInt8
>(basegfx::fround( 255 * (1.0 - fTransparency
) ));
2230 const Gdiplus::Color
aTestColor(aTrans
, maLineColor
.GetRed(), maLineColor
.GetGreen(), maLineColor
.GetBlue());
2231 Gdiplus::Pen
aPen(aTestColor
.GetValue(), Gdiplus::REAL(fLineWidth
));
2232 bool bNoLineJoin(false);
2234 // Set full (Object-to-Device) transformation - if used
2235 if(bObjectToDeviceIsIdentity
)
2237 aGraphics
.ResetTransform();
2241 Gdiplus::Matrix aMatrix
;
2243 aMatrix
.SetElements(
2244 rObjectToDevice
.get(0, 0),
2245 rObjectToDevice
.get(1, 0),
2246 rObjectToDevice
.get(0, 1),
2247 rObjectToDevice
.get(1, 1),
2248 rObjectToDevice
.get(0, 2),
2249 rObjectToDevice
.get(1, 2));
2250 aGraphics
.SetTransform(&aMatrix
);
2255 case basegfx::B2DLineJoin::NONE
:
2263 case basegfx::B2DLineJoin::Bevel
:
2265 aPen
.SetLineJoin(Gdiplus::LineJoinBevel
);
2268 case basegfx::B2DLineJoin::Miter
:
2270 const Gdiplus::REAL
aMiterLimit(1.0/sin(fMiterMinimumAngle
/2.0));
2272 aPen
.SetMiterLimit(aMiterLimit
);
2273 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2274 // graphics, somewhere clipped in some distance from the edge point, dependent
2275 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2277 aPen
.SetLineJoin(Gdiplus::LineJoinMiterClipped
);
2280 case basegfx::B2DLineJoin::Round
:
2282 aPen
.SetLineJoin(Gdiplus::LineJoinRound
);
2289 default: /*css::drawing::LineCap_BUTT*/
2294 case css::drawing::LineCap_ROUND
:
2296 aPen
.SetStartCap(Gdiplus::LineCapRound
);
2297 aPen
.SetEndCap(Gdiplus::LineCapRound
);
2300 case css::drawing::LineCap_SQUARE
:
2302 aPen
.SetStartCap(Gdiplus::LineCapSquare
);
2303 aPen
.SetEndCap(Gdiplus::LineCapSquare
);
2308 // prepare local instance of Gdiplus::GraphicsPath
2309 std::shared_ptr
<Gdiplus::GraphicsPath
> pGraphicsPath
;
2311 // try to access buffered data
2312 std::shared_ptr
<SystemDependentData_GraphicsPath
> pSystemDependentData_GraphicsPath(
2313 rPolygon
.getSystemDependentData
<SystemDependentData_GraphicsPath
>());
2315 // MM01 need to do line dashing as fallback stuff here now
2316 const double fDotDashLength(nullptr != pStroke
? std::accumulate(pStroke
->begin(), pStroke
->end(), 0.0) : 0.0);
2317 const bool bStrokeUsed(0.0 != fDotDashLength
);
2318 assert(!bStrokeUsed
|| (bStrokeUsed
&& pStroke
));
2320 // MM01 decide if to stroke directly
2321 static bool bDoDirectGDIPlusStroke(true);
2323 // activate to stroke directly
2324 if(bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2326 // tdf#124848 the fix of tdf#130478 that was needed here before
2327 // gets much easier when already handling the hairline case above,
2328 // the back-calculated logical linewidth is already here, just use it.
2329 // Still be careful - a zero LineWidth *should* not happen, but...
2330 std::vector
<Gdiplus::REAL
> aDashArray(pStroke
->size());
2331 const double fFactor(fLineWidth
== 0 ? 1.0 : 1.0 / fLineWidth
);
2333 // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
2334 // dash or dot themselves. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
2335 // and other renders (e.g. Impress slide show), while keeping the total length of the
2337 // Patterns are always a sequence dash space dash space ...
2338 if (eLineCap
!= css::drawing::LineCap_BUTT
)
2340 size_t nSize
= pStroke
->size();
2341 // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
2344 for(size_t a(0); a
< nSize
; a
++)
2346 double fDashLengthRel
= (*pStroke
)[2 * a
] * fFactor
;
2347 double fSpaceLengthRel
= (*pStroke
)[2 * a
+ 1] * fFactor
;
2348 // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
2349 // appearance is different, in case space is too small.
2350 double fCorrect
= fSpaceLengthRel
- 1.0 <= 0 ? fSpaceLengthRel
- 0.01 : 1.0;
2351 aDashArray
[2 * a
] = Gdiplus::REAL(fDashLengthRel
+ fCorrect
);
2352 aDashArray
[2 * a
+ 1] = Gdiplus::REAL(fSpaceLengthRel
- fCorrect
);
2357 for(size_t a(0); a
< pStroke
->size(); a
++)
2359 aDashArray
[a
] = Gdiplus::REAL((*pStroke
)[a
] * fFactor
);
2362 if (eLineCap
== css::drawing::LineCap_ROUND
)
2363 aPen
.SetDashCap(Gdiplus::DashCapRound
);
2365 aPen
.SetDashCap(Gdiplus::DashCapFlat
); // "square" doesn't exist in Gdiplus
2366 aPen
.SetDashOffset(Gdiplus::REAL(0.0));
2367 aPen
.SetDashPattern(aDashArray
.data(), aDashArray
.size());
2370 if(!bDoDirectGDIPlusStroke
&& pSystemDependentData_GraphicsPath
)
2372 // MM01 - check on stroke change. Used against not used, or if oth used,
2373 // equal or different? Triangulation geometry creation depends heavily
2374 // on stroke, independent of being transformation independent
2375 const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath
->getStroke().empty());
2377 if(bStrokeWasUsed
!= bStrokeUsed
2378 || (bStrokeUsed
&& *pStroke
!= pSystemDependentData_GraphicsPath
->getStroke()))
2380 // data invalid, forget
2381 pSystemDependentData_GraphicsPath
.reset();
2385 if(pSystemDependentData_GraphicsPath
)
2387 // check data validity
2388 if (pSystemDependentData_GraphicsPath
->getNoLineJoin() != bNoLineJoin
2389 || bPixelSnapHairline
/*tdf#124700*/)
2391 // data invalid, forget
2392 pSystemDependentData_GraphicsPath
.reset();
2396 if(pSystemDependentData_GraphicsPath
)
2398 // copy buffered data
2399 pGraphicsPath
= pSystemDependentData_GraphicsPath
->getGraphicsPath();
2403 // fill data of buffered data
2404 pGraphicsPath
= std::make_shared
<Gdiplus::GraphicsPath
>();
2406 if(!bDoDirectGDIPlusStroke
&& bStrokeUsed
)
2408 // MM01 need to do line dashing as fallback stuff here now
2409 basegfx::B2DPolyPolygon aPolyPolygonLine
;
2412 basegfx::utils::applyLineDashing(
2414 *pStroke
, // pattern
2415 &aPolyPolygonLine
, // target for lines
2416 nullptr, // target for gaps
2417 fDotDashLength
); // full length if available
2419 // MM01 checked/verified, ok
2420 for(sal_uInt32
a(0); a
< aPolyPolygonLine
.count(); a
++)
2422 const basegfx::B2DPolygon
aPolyLine(aPolyPolygonLine
.getB2DPolygon(a
));
2423 pGraphicsPath
->StartFigure();
2424 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2429 bPixelSnapHairline
);
2434 // no line dashing or direct stroke, just copy
2435 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2440 bPixelSnapHairline
);
2442 if(rPolygon
.isClosed() && !bNoLineJoin
)
2444 // #i101491# needed to create the correct line joins
2445 pGraphicsPath
->CloseFigure();
2449 // add to buffering mechanism
2450 if (!bPixelSnapHairline
/*tdf#124700*/)
2452 rPolygon
.addOrReplaceSystemDependentData
<SystemDependentData_GraphicsPath
>(
2459 if(mrParent
.getAntiAlias())
2461 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias
);
2465 aGraphics
.SetSmoothingMode(Gdiplus::SmoothingModeNone
);
2468 if(mrParent
.isPrinter())
2470 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2471 // (look for 'One more hint: This *may* also be needed now in'...).
2472 // See comments in same spot above *urgently* before doing changes here,
2473 // these comments are *still fully valid* at this place (!)
2474 const Gdiplus::REAL
aDpiX(aGraphics
.GetDpiX());
2475 const Gdiplus::REAL
aDpiY(aGraphics
.GetDpiY());
2477 aGraphics
.ScaleTransform(
2478 Gdiplus::REAL(100.0) / aDpiX
,
2479 Gdiplus::REAL(100.0) / aDpiY
,
2480 Gdiplus::MatrixOrderAppend
);
2490 static void paintToGdiPlus(
2491 Gdiplus::Graphics
& rGraphics
,
2492 const SalTwoRect
& rTR
,
2493 Gdiplus::Bitmap
& rBitmap
)
2495 // only parts of source are used
2496 Gdiplus::PointF aDestPoints
[3];
2497 Gdiplus::ImageAttributes aAttributes
;
2499 // define target region as parallelogram
2500 aDestPoints
[0].X
= Gdiplus::REAL(rTR
.mnDestX
);
2501 aDestPoints
[0].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2502 aDestPoints
[1].X
= Gdiplus::REAL(rTR
.mnDestX
+ rTR
.mnDestWidth
);
2503 aDestPoints
[1].Y
= Gdiplus::REAL(rTR
.mnDestY
);
2504 aDestPoints
[2].X
= Gdiplus::REAL(rTR
.mnDestX
);
2505 aDestPoints
[2].Y
= Gdiplus::REAL(rTR
.mnDestY
+ rTR
.mnDestHeight
);
2507 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2509 rGraphics
.DrawImage(
2513 Gdiplus::REAL(rTR
.mnSrcX
),
2514 Gdiplus::REAL(rTR
.mnSrcY
),
2515 Gdiplus::REAL(rTR
.mnSrcWidth
),
2516 Gdiplus::REAL(rTR
.mnSrcHeight
),
2521 static void setInterpolationMode(
2522 Gdiplus::Graphics
& rGraphics
,
2523 tools::Long rSrcWidth
,
2524 tools::Long rDestWidth
,
2525 tools::Long rSrcHeight
,
2526 tools::Long rDestHeight
)
2528 const bool bSameWidth(rSrcWidth
== rDestWidth
);
2529 const bool bSameHeight(rSrcHeight
== rDestHeight
);
2531 if(bSameWidth
&& bSameHeight
)
2533 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeInvalid
);
2535 else if(rDestWidth
> rSrcWidth
&& rDestHeight
> rSrcHeight
)
2537 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2539 else if(rDestWidth
< rSrcWidth
&& rDestHeight
< rSrcHeight
)
2541 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeBicubic
);
2545 rGraphics
.SetInterpolationMode(Gdiplus::InterpolationModeDefault
);
2549 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect
& rTR
, const SalBitmap
& rSrcBitmap
)
2551 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2553 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2555 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2556 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap());
2560 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2562 setInterpolationMode(
2581 bool WinSalGraphicsImpl::blendBitmap(
2588 bool WinSalGraphicsImpl::blendAlphaBitmap(
2597 bool WinSalGraphicsImpl::drawAlphaBitmap(
2598 const SalTwoRect
& rTR
,
2599 const SalBitmap
& rSrcBitmap
,
2600 const SalBitmap
& rAlphaBmp
)
2602 if(rTR
.mnSrcWidth
&& rTR
.mnSrcHeight
&& rTR
.mnDestWidth
&& rTR
.mnDestHeight
)
2604 assert(dynamic_cast<const WinSalBitmap
*>(&rSrcBitmap
));
2605 assert(dynamic_cast<const WinSalBitmap
*>(&rAlphaBmp
));
2607 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSrcBitmap
);
2608 const WinSalBitmap
& rSalAlpha
= static_cast< const WinSalBitmap
& >(rAlphaBmp
);
2609 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(&rSalAlpha
));
2613 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2615 setInterpolationMode(
2634 bool WinSalGraphicsImpl::drawTransformedBitmap(
2635 const basegfx::B2DPoint
& rNull
,
2636 const basegfx::B2DPoint
& rX
,
2637 const basegfx::B2DPoint
& rY
,
2638 const SalBitmap
& rSourceBitmap
,
2639 const SalBitmap
* pAlphaBitmap
,
2642 assert(dynamic_cast<const WinSalBitmap
*>(&rSourceBitmap
));
2643 assert(!pAlphaBitmap
|| dynamic_cast<const WinSalBitmap
*>(pAlphaBitmap
));
2648 const WinSalBitmap
& rSalBitmap
= static_cast< const WinSalBitmap
& >(rSourceBitmap
);
2649 const WinSalBitmap
* pSalAlpha
= static_cast< const WinSalBitmap
* >(pAlphaBitmap
);
2650 std::shared_ptr
< Gdiplus::Bitmap
> aARGB(rSalBitmap
.ImplGetGdiPlusBitmap(pSalAlpha
));
2654 const tools::Long
nSrcWidth(aARGB
->GetWidth());
2655 const tools::Long
nSrcHeight(aARGB
->GetHeight());
2657 if(nSrcWidth
&& nSrcHeight
)
2659 const tools::Long
nDestWidth(basegfx::fround(basegfx::B2DVector(rX
- rNull
).getLength()));
2660 const tools::Long
nDestHeight(basegfx::fround(basegfx::B2DVector(rY
- rNull
).getLength()));
2662 if(nDestWidth
&& nDestHeight
)
2664 Gdiplus::Graphics
aGraphics(mrParent
.getHDC());
2665 Gdiplus::PointF aDestPoints
[3];
2666 Gdiplus::ImageAttributes aAttributes
;
2668 setInterpolationMode(
2675 // this mode is only capable of drawing the whole bitmap to a parallelogram
2676 aDestPoints
[0].X
= Gdiplus::REAL(rNull
.getX());
2677 aDestPoints
[0].Y
= Gdiplus::REAL(rNull
.getY());
2678 aDestPoints
[1].X
= Gdiplus::REAL(rX
.getX());
2679 aDestPoints
[1].Y
= Gdiplus::REAL(rX
.getY());
2680 aDestPoints
[2].X
= Gdiplus::REAL(rY
.getX());
2681 aDestPoints
[2].Y
= Gdiplus::REAL(rY
.getY());
2683 aAttributes
.SetWrapMode(Gdiplus::WrapModeTileFlipXY
);
2685 aGraphics
.DrawImage(
2691 Gdiplus::REAL(nSrcWidth
),
2692 Gdiplus::REAL(nSrcHeight
),
2704 bool WinSalGraphicsImpl::hasFastDrawTransformedBitmap() const
2709 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon
& /*rPolygon*/,
2710 const Gradient
& /*rGradient*/)
2715 bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon
const & /*rPolyPolygon*/,
2716 SalGradient
const & /*rGradient*/)
2721 bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType eType
) const
2723 static bool bAllowForTest(true);
2728 case OutDevSupportType::TransparentRect
:
2729 bRet
= mrParent
.mbVirDev
|| mrParent
.mbWindow
;
2731 case OutDevSupportType::B2DDraw
:
2732 bRet
= bAllowForTest
;
2740 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */