xref: /core/vcl/win/gdi/gdiimpl.cxx (revision 9ed572baa1916a8633cbd6e14ccaffee17ae8c30)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <sal/config.h>
21 
22 #include <cstdlib>
23 #include <memory>
24 #include <numeric>
25 
26 #include <svsys.h>
27 
28 #include "gdiimpl.hxx"
29 
30 #include <string.h>
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 <comphelper/windowserrorstring.hxx>
38 #include <win/wincomp.hxx>
39 #include <win/saldata.hxx>
40 #include <win/salgdi.h>
41 #include <win/salbmp.h>
42 #include <win/scoped_gdi.hxx>
43 #include <vcl/BitmapAccessMode.hxx>
44 #include <vcl/BitmapBuffer.hxx>
45 #include <vcl/BitmapPalette.hxx>
46 #include <win/salframe.h>
47 #include <basegfx/matrix/b2dhommatrixtools.hxx>
48 #include <basegfx/utils/systemdependentdata.hxx>
49 
50 #include <win/salids.hrc>
51 #include <ControlCacheKey.hxx>
52 
53 #include <prewin.h>
54 
55 #include <gdiplus.h>
56 #include <gdiplusenums.h>
57 #include <gdipluscolor.h>
58 #include <Gdipluspixelformats.h>
59 
60 #include <postwin.h>
61 
62 #define SAL_POLYPOLYCOUNT_STACKBUF          8
63 #define SAL_POLYPOLYPOINTS_STACKBUF         64
64 
65 #define SAL_POLY_STACKBUF       32
66 
67 namespace {
68 
69 // #100127# Fill point and flag memory from array of points which
70 // might also contain bezier control points for the PolyDraw() GDI method
71 // Make sure pWinPointAry and pWinFlagAry are big enough
ImplPreparePolyDraw(sal_uLong nPoly,const sal_uInt32 * pPoints,const Point * const * pPtAry,const PolyFlags * const * pFlgAry,POINT * pWinPointAry,BYTE * pWinFlagAry)72 void ImplPreparePolyDraw( sal_uLong                 nPoly,
73                           const sal_uInt32*         pPoints,
74                           const Point* const*    pPtAry,
75                           const PolyFlags* const*   pFlgAry,
76                           POINT*                    pWinPointAry,
77                           BYTE*                     pWinFlagAry     )
78 {
79     sal_uLong nCurrPoly;
80     for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
81     {
82         const Point* pCurrPoint = *pPtAry++;
83         const PolyFlags* pCurrFlag = *pFlgAry++;
84         const sal_uInt32 nCurrPoints = *pPoints++;
85         const bool bHaveFlagArray( pCurrFlag );
86         sal_uLong nCurrPoint;
87 
88         if( nCurrPoints )
89         {
90             // start figure
91             *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
92             pCurrPoint++;
93             *pWinFlagAry++  = PT_MOVETO;
94             ++pCurrFlag;
95 
96             for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
97             {
98                 // #102067# Check existence of flag array
99                 if( bHaveFlagArray &&
100                     ( nCurrPoint + 2 ) < nCurrPoints )
101                 {
102                     PolyFlags P4( pCurrFlag[ 2 ] );
103 
104                     if( ( PolyFlags::Control == pCurrFlag[ 0 ] ) &&
105                         ( PolyFlags::Control == pCurrFlag[ 1 ] ) &&
106                         ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
107                     {
108                         // control point one
109                         *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
110                         pCurrPoint++;
111                         *pWinFlagAry++  = PT_BEZIERTO;
112 
113                         // control point two
114                         *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
115                         pCurrPoint++;
116                         *pWinFlagAry++  = PT_BEZIERTO;
117 
118                         // end point
119                         *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
120                         pCurrPoint++;
121                         *pWinFlagAry++  = PT_BEZIERTO;
122 
123                         nCurrPoint += 3;
124                         pCurrFlag += 3;
125                         continue;
126                     }
127                 }
128 
129                 // regular line point
130                 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
131                 pCurrPoint++;
132                 *pWinFlagAry++  = PT_LINETO;
133                 ++pCurrFlag;
134                 ++nCurrPoint;
135             }
136 
137             // end figure
138             pWinFlagAry[-1] |= PT_CLOSEFIGURE;
139         }
140     }
141 }
142 
ImplGetROPColor(SalROPColor nROPColor)143 Color ImplGetROPColor( SalROPColor nROPColor )
144 {
145     Color nColor;
146     if ( nROPColor == SalROPColor::N0 )
147         nColor = Color( 0, 0, 0 );
148     else
149         nColor = Color( 255, 255, 255 );
150     return nColor;
151 }
152 
153 } // namespace
154 
WinSalGraphicsImpl(WinSalGraphics & rParent)155 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics& rParent):
156     mrParent(rParent),
157     mbXORMode(false),
158     mbPen(false),
159     mhPen(nullptr),
160     mbStockPen(false),
161     mbBrush(false),
162     mbStockBrush(false),
163     mhBrush(nullptr)
164 {
165 }
166 
~WinSalGraphicsImpl()167 WinSalGraphicsImpl::~WinSalGraphicsImpl()
168 {
169     if ( mhPen )
170     {
171         if ( !mbStockPen )
172             DeletePen( mhPen );
173     }
174 
175     if ( mhBrush )
176     {
177         if ( !mbStockBrush )
178             DeleteBrush( mhBrush );
179     }
180 }
181 
copyBits(const SalTwoRect & rPosAry,SalGraphics * pSrcGraphics)182 void WinSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
183 {
184     HDC     hSrcDC;
185     DWORD   nRop;
186 
187     if ( pSrcGraphics )
188         hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->getHDC();
189     else
190         hSrcDC = mrParent.getHDC();
191 
192     if ( mbXORMode )
193         nRop = SRCINVERT;
194     else
195         nRop = SRCCOPY;
196 
197     if ( (rPosAry.mnSrcWidth  == rPosAry.mnDestWidth) &&
198          (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
199     {
200         BitBlt( mrParent.getHDC(),
201                 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
202                 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
203                 hSrcDC,
204                 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
205                 nRop );
206     }
207     else
208     {
209         int nOldStretchMode = SetStretchBltMode( mrParent.getHDC(), STRETCH_DELETESCANS );
210         StretchBlt( mrParent.getHDC(),
211                     static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
212                     static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
213                     hSrcDC,
214                     static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
215                     static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
216                     nRop );
217         SetStretchBltMode( mrParent.getHDC(), nOldStretchMode );
218     }
219 }
220 
221 namespace
222 {
223 
MakeInvisibleArea(const RECT & rSrcRect,int nLeft,int nTop,int nRight,int nBottom,HRGN & rhInvalidateRgn)224 void MakeInvisibleArea(const RECT& rSrcRect,
225                        int nLeft, int nTop, int nRight, int nBottom,
226                        HRGN& rhInvalidateRgn)
227 {
228     if (!rhInvalidateRgn)
229     {
230         rhInvalidateRgn = CreateRectRgnIndirect(&rSrcRect);
231     }
232 
233     ScopedHRGN hTempRgn(CreateRectRgn(nLeft, nTop, nRight, nBottom));
234     CombineRgn(rhInvalidateRgn, rhInvalidateRgn, hTempRgn.get(), RGN_DIFF);
235 }
236 
ImplCalcOutSideRgn(const RECT & rSrcRect,int nLeft,int nTop,int nRight,int nBottom,HRGN & rhInvalidateRgn)237 void ImplCalcOutSideRgn( const RECT& rSrcRect,
238                          int nLeft, int nTop, int nRight, int nBottom,
239                          HRGN& rhInvalidateRgn )
240 {
241     // calculate area outside the visible region
242     if (rSrcRect.left < nLeft)
243     {
244         MakeInvisibleArea(rSrcRect, -31999, 0, nLeft, 31999, rhInvalidateRgn);
245     }
246     if (rSrcRect.top < nTop)
247     {
248         MakeInvisibleArea(rSrcRect, 0, -31999, 31999, nTop, rhInvalidateRgn);
249     }
250     if (rSrcRect.right > nRight)
251     {
252         MakeInvisibleArea(rSrcRect, nRight, 0, 31999, 31999, rhInvalidateRgn);
253     }
254     if (rSrcRect.bottom > nBottom)
255     {
256         MakeInvisibleArea(rSrcRect, 0, nBottom, 31999, 31999, rhInvalidateRgn);
257     }
258 }
259 
260 } // namespace
261 
copyArea(tools::Long nDestX,tools::Long nDestY,tools::Long nSrcX,tools::Long nSrcY,tools::Long nSrcWidth,tools::Long nSrcHeight,bool bWindowInvalidate)262 void WinSalGraphicsImpl::copyArea( tools::Long nDestX, tools::Long nDestY,
263                             tools::Long nSrcX, tools::Long nSrcY,
264                             tools::Long nSrcWidth, tools::Long nSrcHeight,
265                             bool bWindowInvalidate )
266 {
267     bool    bRestoreClipRgn = false;
268     HRGN    hOldClipRgn = nullptr;
269     int     nOldClipRgnType = ERROR;
270     HRGN    hInvalidateRgn = nullptr;
271 
272     // do we have to invalidate also the overlapping regions?
273     if ( bWindowInvalidate && mrParent.isWindow() )
274     {
275         // compute and invalidate those parts that were either off-screen or covered by other windows
276         //  while performing the above BitBlt
277         // those regions then have to be invalidated as they contain useless/wrong data
278         RECT    aSrcRect;
279         RECT    aClipRect;
280         RECT    aTempRect;
281         RECT    aTempRect2;
282         HRGN    hTempRgn;
283         HWND    hWnd;
284 
285         // restrict srcRect to this window (calc intersection)
286         aSrcRect.left   = static_cast<int>(nSrcX);
287         aSrcRect.top    = static_cast<int>(nSrcY);
288         aSrcRect.right  = aSrcRect.left+static_cast<int>(nSrcWidth);
289         aSrcRect.bottom = aSrcRect.top+static_cast<int>(nSrcHeight);
290         GetClientRect( mrParent.gethWnd(), &aClipRect );
291         if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
292         {
293             // transform srcRect to screen coordinates
294             POINT aPt;
295             aPt.x = 0;
296             aPt.y = 0;
297             ClientToScreen( mrParent.gethWnd(), &aPt );
298             aSrcRect.left   += aPt.x;
299             aSrcRect.top    += aPt.y;
300             aSrcRect.right  += aPt.x;
301             aSrcRect.bottom += aPt.y;
302             hInvalidateRgn = nullptr;
303 
304             // compute the parts that are off screen (ie invisible)
305             RECT theScreen;
306             ImplSalGetWorkArea( nullptr, &theScreen, nullptr );  // find the screen area taking multiple monitors into account
307             ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
308 
309             // calculate regions that are covered by other windows
310             HRGN hTempRgn2 = nullptr;
311             HWND hWndTopWindow = mrParent.gethWnd();
312             // Find the TopLevel Window, because only Windows which are in
313             // in the foreground of our TopLevel window must be considered
314             if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
315             {
316                 RECT aTempRect3 = aSrcRect;
317                 do
318                 {
319                     hWndTopWindow = ::GetParent( hWndTopWindow );
320 
321                     // Test if the Parent clips our window
322                     GetClientRect( hWndTopWindow, &aTempRect );
323                     POINT aPt2;
324                     aPt2.x = 0;
325                     aPt2.y = 0;
326                     ClientToScreen( hWndTopWindow, &aPt2 );
327                     aTempRect.left   += aPt2.x;
328                     aTempRect.top    += aPt2.y;
329                     aTempRect.right  += aPt2.x;
330                     aTempRect.bottom += aPt2.y;
331                     IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
332                 }
333                 while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
334 
335                 // If one or more Parents clip our window, then we must
336                 // calculate the outside area
337                 if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
338                 {
339                     ImplCalcOutSideRgn( aSrcRect,
340                                         aTempRect3.left, aTempRect3.top,
341                                         aTempRect3.right, aTempRect3.bottom,
342                                         hInvalidateRgn );
343                 }
344             }
345             // retrieve the top-most (z-order) child window
346             hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
347             while ( hWnd )
348             {
349                 if ( hWnd == hWndTopWindow )
350                     break;
351                 if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
352                 {
353                     GetWindowRect( hWnd, &aTempRect );
354                     if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
355                     {
356                         // hWnd covers part or all of aSrcRect
357                         if ( !hInvalidateRgn )
358                             hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
359 
360                         // get full bounding box of hWnd
361                         hTempRgn = CreateRectRgnIndirect( &aTempRect );
362 
363                         // get region of hWnd (the window may be shaped)
364                         if ( !hTempRgn2 )
365                             hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
366                         int nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
367                         if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
368                         {
369                             // convert window region to screen coordinates
370                             OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
371                             // and intersect with the window's bounding box
372                             CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
373                         }
374                         // finally compute that part of aSrcRect which is not covered by any parts of hWnd
375                         CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
376                         DeleteRegion( hTempRgn );
377                     }
378                 }
379                 // retrieve the next window in the z-order, i.e. the window below hwnd
380                 hWnd = GetWindow( hWnd, GW_HWNDNEXT );
381             }
382             if ( hTempRgn2 )
383                 DeleteRegion( hTempRgn2 );
384             if ( hInvalidateRgn )
385             {
386                 // hInvalidateRgn contains the fully visible parts of the original srcRect
387                 hTempRgn = CreateRectRgnIndirect( &aSrcRect );
388                 // subtract it from the original rect to get the occluded parts
389                 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
390                 DeleteRegion( hTempRgn );
391 
392                 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
393                 {
394                     // move the occluded parts to the destination pos
395                     int nOffX = static_cast<int>(nDestX-nSrcX);
396                     int nOffY = static_cast<int>(nDestY-nSrcY);
397                     OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
398 
399                     // by excluding hInvalidateRgn from the system's clip region
400                     // we will prevent bitblt from copying useless data
401                     // especially now shadows from overlapping windows will appear (#i36344)
402                     hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
403                     nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
404 
405                     bRestoreClipRgn = true; // indicate changed clipregion and force invalidate
406                     ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
407                 }
408             }
409         }
410     }
411 
412     BitBlt( mrParent.getHDC(),
413             static_cast<int>(nDestX), static_cast<int>(nDestY),
414             static_cast<int>(nSrcWidth), static_cast<int>(nSrcHeight),
415             mrParent.getHDC(),
416             static_cast<int>(nSrcX), static_cast<int>(nSrcY),
417             SRCCOPY );
418 
419     if( bRestoreClipRgn )
420     {
421         // restore old clip region
422         if( nOldClipRgnType != ERROR )
423             SelectClipRgn( mrParent.getHDC(), hOldClipRgn);
424         DeleteRegion( hOldClipRgn );
425 
426         // invalidate regions that were not copied
427         bool    bInvalidate = true;
428 
429         // Combine Invalidate vcl::Region with existing ClipRegion
430         HRGN    hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
431         if ( GetClipRgn( mrParent.getHDC(), hTempRgn ) == 1 )
432         {
433             int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
434             if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
435                 bInvalidate = false;
436         }
437         DeleteRegion( hTempRgn );
438 
439         if ( bInvalidate )
440         {
441             InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE );
442             // here we only initiate an update if this is the MainThread,
443             // so that there is no deadlock when handling the Paint event,
444             // as the SolarMutex is already held by this Thread
445             SalData*    pSalData = GetSalData();
446             DWORD       nCurThreadId = GetCurrentThreadId();
447             if ( pSalData->mnAppThreadId == nCurThreadId )
448                 UpdateWindow( mrParent.gethWnd() );
449         }
450 
451         DeleteRegion( hInvalidateRgn );
452     }
453 
454 }
455 
456 namespace {
457 
ImplDrawBitmap(HDC hDC,const SalTwoRect & rPosAry,const WinSalBitmap & rSalBitmap,bool bPrinter,int nDrawMode)458 void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSalBitmap,
459                      bool bPrinter, int nDrawMode )
460 {
461     if( !hDC )
462         return;
463 
464     HGLOBAL     hDrawDIB;
465     HBITMAP     hDrawDDB = rSalBitmap.ImplGethDDB();
466     std::optional<WinSalBitmap> xTmpSalBmp;
467     bool        bPrintDDB = ( bPrinter && hDrawDDB );
468 
469     if( bPrintDDB )
470     {
471         xTmpSalBmp.emplace();
472         xTmpSalBmp->Create(rSalBitmap, vcl::bitDepthToPixelFormat(rSalBitmap.GetBitCount()));
473         hDrawDIB = xTmpSalBmp->ImplGethDIB();
474     }
475     else
476         hDrawDIB = rSalBitmap.ImplGethDIB();
477 
478     if( hDrawDIB )
479     {
480         if (PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB )))
481         {
482             PBYTE               pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
483                                         WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
484             const int           nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
485 
486             StretchDIBits( hDC,
487                            static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
488                            static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
489                            static_cast<int>(rPosAry.mnSrcX), static_cast<int>(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY),
490                            static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
491                            pBits, pBI, DIB_RGB_COLORS, nDrawMode );
492 
493             GlobalUnlock( hDrawDIB );
494             SetStretchBltMode( hDC, nOldStretchMode );
495         }
496     }
497     else if( hDrawDDB && !bPrintDDB )
498     {
499         ScopedCachedHDC<CACHED_HDC_DRAW> hBmpDC(hDrawDDB);
500 
501         COLORREF    nOldBkColor = RGB(0xFF,0xFF,0xFF);
502         COLORREF    nOldTextColor = RGB(0,0,0);
503         bool        bMono = ( rSalBitmap.GetBitCount() == 1 );
504 
505         if( bMono )
506         {
507             COLORREF nBkColor = RGB( 0xFF, 0xFF, 0xFF );
508             COLORREF nTextColor = RGB( 0x00, 0x00, 0x00 );
509             //fdo#33455 handle 1 bit depth pngs with palette entries
510             //to set fore/back colors
511             if (BitmapBuffer* pBitmapBuffer = const_cast<WinSalBitmap&>(rSalBitmap).AcquireBuffer(BitmapAccessMode::Info))
512             {
513                 const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
514                 if (rPalette.GetEntryCount() == 2)
515                 {
516                     Color nCol = rPalette[0];
517                     nTextColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
518                     nCol = rPalette[1];
519                     nBkColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
520                 }
521                 const_cast<WinSalBitmap&>(rSalBitmap).ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Info);
522             }
523             nOldBkColor = SetBkColor( hDC, nBkColor );
524             nOldTextColor = ::SetTextColor( hDC, nTextColor );
525         }
526 
527         if ( (rPosAry.mnSrcWidth  == rPosAry.mnDestWidth) &&
528              (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
529         {
530             BitBlt( hDC,
531                     static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
532                     static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
533                     hBmpDC.get(),
534                     static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
535                     nDrawMode );
536         }
537         else
538         {
539             const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
540 
541             StretchBlt( hDC,
542                         static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
543                         static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
544                         hBmpDC.get(),
545                         static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
546                         static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
547                         nDrawMode );
548 
549             SetStretchBltMode( hDC, nOldStretchMode );
550         }
551 
552         if( bMono )
553         {
554             SetBkColor( hDC, nOldBkColor );
555             ::SetTextColor( hDC, nOldTextColor );
556         }
557     }
558 }
559 
560 } // namespace
561 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSalBitmap)562 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
563 {
564     bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
565 
566     if(bTryDirectPaint)
567     {
568         // only paint direct when no scaling and no MapMode, else the
569         // more expensive conversions may be done for short-time Bitmap/BitmapEx
570         // used for buffering only
571         if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
572         {
573             bTryDirectPaint = false;
574         }
575     }
576 
577     // try to draw using GdiPlus directly
578     if(bTryDirectPaint && TryDrawBitmapGDIPlus(rPosAry, rSalBitmap))
579     {
580         return;
581     }
582 
583     // fall back old stuff
584     assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
585 
586     ImplDrawBitmap(mrParent.getHDC(), rPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
587         mrParent.isPrinter(),
588         mbXORMode ? SRCINVERT : SRCCOPY );
589 }
590 
drawBitmap(const SalTwoRect & rPosAry,const SalBitmap & rSSalBitmap,const SalBitmap & rSTransparentBitmap)591 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
592                               const SalBitmap& rSSalBitmap,
593                               const SalBitmap& rSTransparentBitmap )
594 {
595     SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
596     bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
597 
598     // try to draw using GdiPlus directly
599     if(bTryDirectPaint && drawAlphaBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap))
600     {
601         return;
602     }
603 
604     assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
605     assert(dynamic_cast<const WinSalBitmap*>(&rSTransparentBitmap));
606 
607     const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
608     const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
609 
610     SalTwoRect  aPosAry = rPosAry;
611     int         nDstX = static_cast<int>(aPosAry.mnDestX);
612     int         nDstY = static_cast<int>(aPosAry.mnDestY);
613     int         nDstWidth = static_cast<int>(aPosAry.mnDestWidth);
614     int         nDstHeight = static_cast<int>(aPosAry.mnDestHeight);
615     HDC         hDC = mrParent.getHDC();
616 
617     ScopedHBITMAP hMemBitmap;
618     ScopedHBITMAP hMaskBitmap;
619 
620     if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
621     {
622         hMemBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
623         hMaskBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
624     }
625 
626     ScopedCachedHDC<CACHED_HDC_1> hMemDC(hMemBitmap.get());
627     ScopedCachedHDC<CACHED_HDC_2> hMaskDC(hMaskBitmap.get());
628 
629     aPosAry.mnDestX = aPosAry.mnDestY = 0;
630     BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
631 
632     // WIN/WNT seems to have a minor problem mapping the correct color of the
633     // mask to the palette if we draw the DIB directly ==> draw DDB
634     if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
635     {
636         WinSalBitmap aTmp;
637 
638         if( aTmp.Create( rTransparentBitmap, &mrParent ) )
639             ImplDrawBitmap( hMaskDC.get(), aPosAry, aTmp, false, SRCCOPY );
640     }
641     else
642         ImplDrawBitmap( hMaskDC.get(), aPosAry, rTransparentBitmap, false, SRCCOPY );
643 
644     // now MemDC contains background, MaskDC the transparency mask
645 
646     // #105055# Respect XOR mode
647     if( mbXORMode )
648     {
649         ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
650         // now MaskDC contains the bitmap area with black background
651         BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCINVERT );
652         // now MemDC contains background XORed bitmap area on top
653     }
654     else
655     {
656         BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCAND );
657         // now MemDC contains background with masked-out bitmap area
658         ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
659         // now MaskDC contains the bitmap area with black background
660         BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCPAINT );
661         // now MemDC contains background and bitmap merged together
662     }
663     // copy to output DC
664     BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC.get(), 0, 0, SRCCOPY );
665 }
666 
drawAlphaRect(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,sal_uInt8 nTransparency)667 bool WinSalGraphicsImpl::drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth,
668                                     tools::Long nHeight, sal_uInt8 nTransparency )
669 {
670     if( mbPen || !mbBrush || mbXORMode )
671         return false; // can only perform solid fills without XOR.
672 
673     ScopedCachedHDC<CACHED_HDC_1> hMemDC(nullptr);
674     SetPixel( hMemDC.get(), int(0), int(0), mnBrushColor );
675 
676     BLENDFUNCTION aFunc = {
677         AC_SRC_OVER,
678         0,
679         sal::static_int_cast<sal_uInt8>(255 - 255L*nTransparency/100),
680         0
681     };
682 
683     // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
684     // that to dest hdc
685     bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
686                               hMemDC.get(), 0,0,1,1,
687                               aFunc ) == TRUE;
688 
689     return bRet;
690 }
691 
drawMask(const SalTwoRect & rPosAry,const SalBitmap & rSSalBitmap,Color nMaskColor)692 void WinSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry,
693                                   const SalBitmap& rSSalBitmap,
694                                   Color nMaskColor)
695 {
696     SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
697 
698     assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
699 
700     const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
701 
702     SalTwoRect  aPosAry = rPosAry;
703     const HDC hDC = mrParent.getHDC();
704 
705     ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(RGB(nMaskColor.GetRed(),
706                                                           nMaskColor.GetGreen(),
707                                                           nMaskColor.GetBlue())));
708 
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 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
712     {
713         WinSalBitmap aTmp;
714 
715         if( aTmp.Create( rSalBitmap, &mrParent ) )
716             ImplDrawBitmap( hDC, aPosAry, aTmp, false, 0x00B8074AUL ); // raster operation PSDPxax
717     }
718     else
719         ImplDrawBitmap( hDC, aPosAry, rSalBitmap, false, 0x00B8074AUL );// raster operation PSDPxax
720 }
721 
getBitmap(tools::Long nX,tools::Long nY,tools::Long nDX,tools::Long nDY,bool bWithoutAlpha)722 std::shared_ptr<SalBitmap> WinSalGraphicsImpl::getBitmap( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY, bool bWithoutAlpha )
723 {
724     SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
725 
726     std::shared_ptr<WinSalBitmap> pSalBitmap;
727 
728     nDX = std::abs( nDX );
729     nDY = std::abs( nDY );
730 
731     HDC     hDC = mrParent.getHDC();
732     HBITMAP hBmpBitmap;
733     if (bWithoutAlpha && GetBitCount() == 32)
734     {
735         BITMAPINFO aBitmapInfo;
736         aBitmapInfo.bmiHeader.biSize = sizeof( BITMAPINFOHEADER );
737         aBitmapInfo.bmiHeader.biWidth = nDX;
738         aBitmapInfo.bmiHeader.biHeight = -nDY; // negative for top down
739         aBitmapInfo.bmiHeader.biPlanes = 1;
740         aBitmapInfo.bmiHeader.biBitCount = 24;
741         aBitmapInfo.bmiHeader.biCompression = BI_RGB;
742         aBitmapInfo.bmiHeader.biSizeImage = 0;
743         aBitmapInfo.bmiHeader.biXPelsPerMeter = 0;
744         aBitmapInfo.bmiHeader.biYPelsPerMeter = 0;
745         aBitmapInfo.bmiHeader.biClrUsed = 0;
746         aBitmapInfo.bmiHeader.biClrImportant = 0;
747 
748         void* pData;
749         hBmpBitmap = CreateDIBSection( hDC, &aBitmapInfo,
750                                     DIB_RGB_COLORS, &pData, nullptr,
751                                     0 );
752         SAL_WARN_IF( !hBmpBitmap, "vcl", "CreateDIBSection failed: " << comphelper::WindowsErrorString( GetLastError() ) );
753     }
754     else
755         hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
756 
757     bool    bRet;
758     {
759         ScopedCachedHDC<CACHED_HDC_1> hBmpDC(hBmpBitmap);
760         bRet = BitBlt(hBmpDC.get(), 0, 0,
761                       static_cast<int>(nDX), static_cast<int>(nDY), hDC,
762                       static_cast<int>(nX), static_cast<int>(nY), SRCCOPY) ? TRUE : FALSE;
763     }
764 
765     if( bRet )
766     {
767         pSalBitmap = std::make_shared<WinSalBitmap>();
768 
769         if( !pSalBitmap->Create( hBmpBitmap ) )
770         {
771             pSalBitmap.reset();
772         }
773     }
774     else
775     {
776         // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
777         DeleteBitmap( hBmpBitmap );
778     }
779 
780     return pSalBitmap;
781 }
782 
getPixel(tools::Long nX,tools::Long nY)783 Color WinSalGraphicsImpl::getPixel( tools::Long nX, tools::Long nY )
784 {
785     // The only way to read a pixel color with alpha is via GDI+
786     // This is all hideously inefficient, but we only really use it for unit tests.
787     Gdiplus::Bitmap screenPixel(1, 1, PixelFormat32bppARGB);
788     Gdiplus::Graphics gdest(&screenPixel);
789     auto hDestDC = gdest.GetHDC();
790     BOOL retval = BitBlt(hDestDC, 0, 0, 1, 1,
791                         mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY),
792                         SRCCOPY);
793     gdest.ReleaseHDC(hDestDC);
794 
795     if (!retval)
796     {
797         SAL_WARN("vcl", "GetPixel failed1");
798         return Color(0, 0, 0);
799     }
800     Gdiplus::Color nGdiColor;
801     if (screenPixel.GetPixel(0, 0, &nGdiColor) != Gdiplus::Ok)
802     {
803         SAL_WARN("vcl", "GetPixel failed2");
804         return Color(0, 0, 0);
805     }
806     // seems to be returning premultiplied color, despite the pixel format that I pass in the constructor
807     if (nGdiColor.GetAlpha() == 0)
808         return Color(ColorAlpha, nGdiColor.GetAlpha(),
809                      nGdiColor.GetRed(),
810                      nGdiColor.GetGreen(),
811                      nGdiColor.GetBlue());
812     else
813         return Color(ColorAlpha, nGdiColor.GetAlpha(),
814                      nGdiColor.GetRed() * 255 / nGdiColor.GetAlpha(),
815                      nGdiColor.GetGreen() * 255 / nGdiColor.GetAlpha(),
816                      nGdiColor.GetBlue() * 255 / nGdiColor.GetAlpha());
817 }
818 
819 namespace
820 {
821 
Get50PercentBrush()822 HBRUSH Get50PercentBrush()
823 {
824     SalData* pSalData = GetSalData();
825     if ( !pSalData->mh50Brush )
826     {
827         if ( !pSalData->mh50Bmp )
828             pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
829         pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
830     }
831 
832     return pSalData->mh50Brush;
833 }
834 
835 } // namespace
836 
invert(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight,SalInvert nFlags)837 void WinSalGraphicsImpl::invert( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags )
838 {
839     if ( nFlags & SalInvert::TrackFrame )
840     {
841         HPEN    hDotPen = CreatePen( PS_DOT, 0, 0 );
842         HPEN    hOldPen = SelectPen( mrParent.getHDC(), hDotPen );
843         HBRUSH  hOldBrush = SelectBrush( mrParent.getHDC(), GetStockBrush( NULL_BRUSH ) );
844         int     nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
845 
846         Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
847 
848         SetROP2( mrParent.getHDC(), nOldROP );
849         SelectPen( mrParent.getHDC(), hOldPen );
850         SelectBrush( mrParent.getHDC(), hOldBrush );
851         DeletePen( hDotPen );
852     }
853     else if ( nFlags & SalInvert::N50 )
854     {
855         COLORREF nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
856         HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), Get50PercentBrush() );
857         PatBlt( mrParent.getHDC(), nX, nY, nWidth, nHeight, PATINVERT );
858         ::SetTextColor( mrParent.getHDC(), nOldTextColor );
859         SelectBrush( mrParent.getHDC(), hOldBrush );
860     }
861     else
862     {
863          RECT aRect;
864          aRect.left      = static_cast<int>(nX);
865          aRect.top       = static_cast<int>(nY);
866          aRect.right     = static_cast<int>(nX)+nWidth;
867          aRect.bottom    = static_cast<int>(nY)+nHeight;
868          ::InvertRect( mrParent.getHDC(), &aRect );
869     }
870 }
871 
invert(sal_uInt32 nPoints,const Point * pPtAry,SalInvert nSalFlags)872 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nSalFlags )
873 {
874     HPEN        hPen;
875     HPEN        hOldPen;
876     HBRUSH      hBrush;
877     HBRUSH      hOldBrush = nullptr;
878     COLORREF    nOldTextColor RGB(0,0,0);
879     int         nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
880 
881     if ( nSalFlags & SalInvert::TrackFrame )
882         hPen = CreatePen( PS_DOT, 0, 0 );
883     else
884     {
885 
886         if ( nSalFlags & SalInvert::N50 )
887             hBrush = Get50PercentBrush();
888         else
889             hBrush = GetStockBrush( BLACK_BRUSH );
890 
891         hPen = GetStockPen( NULL_PEN );
892         nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
893         hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
894     }
895     hOldPen = SelectPen( mrParent.getHDC(), hPen );
896 
897     std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
898     for (sal_uInt32 i=0; i<nPoints; ++i)
899         pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
900 
901     if ( nSalFlags & SalInvert::TrackFrame )
902         Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) );
903     else
904         Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) );
905 
906     SetROP2( mrParent.getHDC(), nOldROP );
907     SelectPen( mrParent.getHDC(), hOldPen );
908 
909     if ( nSalFlags & SalInvert::TrackFrame )
910         DeletePen( hPen );
911     else
912     {
913         ::SetTextColor( mrParent.getHDC(), nOldTextColor );
914         SelectBrush( mrParent.getHDC(), hOldBrush );
915     }
916 }
917 
GetBitCount() const918 sal_uInt16 WinSalGraphicsImpl::GetBitCount() const
919 {
920     return static_cast<sal_uInt16>(GetDeviceCaps( mrParent.getHDC(), BITSPIXEL ));
921 }
922 
GetGraphicsWidth() const923 tools::Long WinSalGraphicsImpl::GetGraphicsWidth() const
924 {
925     if( !mrParent.gethWnd() || !IsWindow( mrParent.gethWnd() ) )
926         return 0;
927 
928     WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
929     if( !pFrame )
930         return 0;
931 
932     if (pFrame->GetWidth())
933         return pFrame->GetWidth();
934 
935     // TODO: perhaps not needed, width should always be up-to-date
936     RECT aRect;
937     GetClientRect( mrParent.gethWnd(), &aRect );
938     return aRect.right;
939 }
940 
ResetClipRegion()941 void WinSalGraphicsImpl::ResetClipRegion()
942 {
943     if ( mrParent.mhRegion )
944     {
945         DeleteRegion( mrParent.mhRegion );
946         mrParent.mhRegion = nullptr;
947     }
948 
949     SelectClipRgn( mrParent.getHDC(), nullptr );
950 }
951 
containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon & rCandidate)952 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon& rCandidate)
953 {
954     if(rCandidate.areControlPointsUsed())
955         return false;
956 
957     const sal_uInt32 nPointCount(rCandidate.count());
958 
959     if(nPointCount < 2)
960         return true;
961 
962     const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount + 1 : nPointCount);
963     basegfx::B2DPoint aLast(rCandidate.getB2DPoint(0));
964 
965     for(sal_uInt32 a(1); a < nEdgeCount; a++)
966     {
967         const sal_uInt32 nNextIndex(a % nPointCount);
968         const basegfx::B2DPoint aCurrent(rCandidate.getB2DPoint(nNextIndex));
969 
970         if(!basegfx::fTools::equal(aLast.getX(), aCurrent.getX()) && !basegfx::fTools::equal(aLast.getY(), aCurrent.getY()))
971         {
972             return false;
973         }
974 
975         aLast = aCurrent;
976     }
977 
978     return true;
979 }
980 
containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon & rCandidate)981 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon& rCandidate)
982 {
983     if(rCandidate.areControlPointsUsed())
984         return false;
985 
986     for(auto const& rPolygon : rCandidate)
987     {
988         if(!containsOnlyHorizontalAndVerticalEdges(rPolygon))
989         {
990             return false;
991         }
992     }
993 
994     return true;
995 }
996 
setClipRegion(const vcl::Region & i_rClip)997 void WinSalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip )
998 {
999     if ( mrParent.mhRegion )
1000     {
1001         DeleteRegion( mrParent.mhRegion );
1002         mrParent.mhRegion = nullptr;
1003     }
1004 
1005     bool bUsePolygon(i_rClip.HasPolyPolygonOrB2DPolyPolygon());
1006     static bool bTryToAvoidPolygon(true);
1007 
1008     // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1009     // and only contains horizontal/vertical edges. In that case, use the fallback
1010     // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1011     // the correct polygon-to-RegionBand transformation.
1012     // Background is that when using the same Rectangle as rectangle or as Polygon
1013     // clip region will lead to different results; the polygon-based one will be
1014     // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1015     // again is because of the polygon-nature and it's classic handling when filling.
1016     // This also means that all cases which use a 'true' polygon-based incarnation of
1017     // a vcl::Region should know what they do - it may lead to repaint errors.
1018     if(bUsePolygon && bTryToAvoidPolygon)
1019     {
1020         const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1021 
1022         if(!aPolyPolygon.areControlPointsUsed())
1023         {
1024             if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon))
1025             {
1026                 bUsePolygon = false;
1027             }
1028         }
1029     }
1030 
1031     if(bUsePolygon)
1032     {
1033         // #i122149# check the comment above to know that this may lead to potential repaint
1034         // problems. It may be solved (if needed) by scaling the polygon by one in X
1035         // and Y. Currently the workaround to only use it if really unavoidable will
1036         // solve most cases. When someone is really using polygon-based Regions he
1037         // should know what he is doing.
1038         // Added code to do that scaling to check if it works, testing it.
1039         const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1040         const sal_uInt32 nCount(aPolyPolygon.count());
1041 
1042         if( nCount )
1043         {
1044             std::vector< POINT > aPolyPoints;
1045             aPolyPoints.reserve( 1024 );
1046             std::vector< INT > aPolyCounts( nCount, 0 );
1047             basegfx::B2DHomMatrix aExpand;
1048             sal_uInt32 nTargetCount(0);
1049             static bool bExpandByOneInXandY(true);
1050 
1051             if(bExpandByOneInXandY)
1052             {
1053                 const basegfx::B2DRange aRangeS(aPolyPolygon.getB2DRange());
1054                 const basegfx::B2DRange aRangeT(aRangeS.getMinimum(), aRangeS.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1055                 aExpand = basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS, aRangeT);
1056             }
1057 
1058             for(auto const& rPolygon : aPolyPolygon)
1059             {
1060                 const basegfx::B2DPolygon aPoly(
1061                     basegfx::utils::adaptiveSubdivideByDistance(
1062                         rPolygon,
1063                         1));
1064                 const sal_uInt32 nPoints(aPoly.count());
1065 
1066                 // tdf#40863 For CustomShapes there is a hack (see
1067                 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1068                 // with a single point in top-left and bottom-right corner
1069                 // of the BoundRect to be able to determine the correct BoundRect
1070                 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1071                 // fails with polygons containing a single pixel, so clipping is
1072                 // lost. For now, use only polygons with more than two points - the
1073                 // ones that may have an area.
1074                 // Note: polygons with one point which are curves may have an area,
1075                 // but the polygon is already subdivided here, so no need to test
1076                 // this.
1077                 if(nPoints > 2)
1078                 {
1079                     aPolyCounts[nTargetCount] = nPoints;
1080                     nTargetCount++;
1081 
1082                     for( sal_uInt32 b = 0; b < nPoints; b++ )
1083                     {
1084                         basegfx::B2DPoint aPt(aPoly.getB2DPoint(b));
1085 
1086                         if(bExpandByOneInXandY)
1087                         {
1088                             aPt = aExpand * aPt;
1089                         }
1090 
1091                         POINT aPOINT;
1092                         // #i122149# do correct rounding
1093                         aPOINT.x = basegfx::fround(aPt.getX());
1094                         aPOINT.y = basegfx::fround(aPt.getY());
1095                         aPolyPoints.push_back( aPOINT );
1096                     }
1097                 }
1098             }
1099 
1100             if(nTargetCount)
1101             {
1102                 mrParent.mhRegion = CreatePolyPolygonRgn( aPolyPoints.data(), aPolyCounts.data(), nTargetCount, ALTERNATE );
1103             }
1104         }
1105     }
1106     else
1107     {
1108         RectangleVector aRectangles;
1109         i_rClip.GetRegionRectangles(aRectangles);
1110 
1111         sal_uLong nRectBufSize = sizeof(RECT)*aRectangles.size();
1112         if ( aRectangles.size() < SAL_CLIPRECT_COUNT )
1113         {
1114             if ( !mrParent.mpStdClipRgnData )
1115                 mrParent.mpStdClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))]);
1116             mrParent.mpClipRgnData = mrParent.mpStdClipRgnData;
1117         }
1118         else
1119             mrParent.mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
1120         mrParent.mpClipRgnData->rdh.dwSize   = sizeof( RGNDATAHEADER );
1121         mrParent.mpClipRgnData->rdh.iType    = RDH_RECTANGLES;
1122         mrParent.mpClipRgnData->rdh.nCount   = aRectangles.size();
1123         mrParent.mpClipRgnData->rdh.nRgnSize = nRectBufSize;
1124         RECT*       pBoundRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1125         SetRectEmpty( pBoundRect );
1126         RECT* pNextClipRect         = reinterpret_cast<RECT*>(&(mrParent.mpClipRgnData->Buffer));
1127         bool bFirstClipRect         = true;
1128 
1129         for (auto const& rectangle : aRectangles)
1130         {
1131             const tools::Long nW(rectangle.GetWidth());
1132             const tools::Long nH(rectangle.GetHeight());
1133 
1134             if(nW && nH)
1135             {
1136                 const tools::Long nRight(rectangle.Left() + nW);
1137                 const tools::Long nBottom(rectangle.Top() + nH);
1138 
1139                 if(bFirstClipRect)
1140                 {
1141                     pBoundRect->left = rectangle.Left();
1142                     pBoundRect->top = rectangle.Top();
1143                     pBoundRect->right = nRight;
1144                     pBoundRect->bottom = nBottom;
1145                     bFirstClipRect = false;
1146                 }
1147                 else
1148                 {
1149                     if(rectangle.Left() < pBoundRect->left)
1150                     {
1151                         pBoundRect->left = static_cast<int>(rectangle.Left());
1152                     }
1153 
1154                     if(rectangle.Top() < pBoundRect->top)
1155                     {
1156                         pBoundRect->top = static_cast<int>(rectangle.Top());
1157                     }
1158 
1159                     if(nRight > pBoundRect->right)
1160                     {
1161                         pBoundRect->right = static_cast<int>(nRight);
1162                     }
1163 
1164                     if(nBottom > pBoundRect->bottom)
1165                     {
1166                         pBoundRect->bottom = static_cast<int>(nBottom);
1167                     }
1168                 }
1169 
1170                 pNextClipRect->left = static_cast<int>(rectangle.Left());
1171                 pNextClipRect->top = static_cast<int>(rectangle.Top());
1172                 pNextClipRect->right = static_cast<int>(nRight);
1173                 pNextClipRect->bottom = static_cast<int>(nBottom);
1174                 pNextClipRect++;
1175             }
1176             else
1177             {
1178                 mrParent.mpClipRgnData->rdh.nCount--;
1179                 mrParent.mpClipRgnData->rdh.nRgnSize -= sizeof( RECT );
1180             }
1181         }
1182 
1183         // create clip region from ClipRgnData
1184         if(0 == mrParent.mpClipRgnData->rdh.nCount)
1185         {
1186             // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1187             // that contains no polygons or only empty ones (no width/height). This is
1188             // perfectly fine and we are done, except setting it (see end of method)
1189         }
1190         else if(1 == mrParent.mpClipRgnData->rdh.nCount)
1191         {
1192             RECT* pRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1193             mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top,
1194                                                      pRect->right, pRect->bottom );
1195         }
1196         else if(mrParent.mpClipRgnData->rdh.nCount > 1)
1197         {
1198             sal_uLong nSize = mrParent.mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
1199             mrParent.mhRegion = ExtCreateRegion( nullptr, nSize, mrParent.mpClipRgnData );
1200 
1201             // if ExtCreateRegion(...) is not supported
1202             if( !mrParent.mhRegion )
1203             {
1204                 RGNDATAHEADER const & pHeader = mrParent.mpClipRgnData->rdh;
1205 
1206                 if( pHeader.nCount )
1207                 {
1208                     RECT* pRect = reinterpret_cast<RECT*>(mrParent.mpClipRgnData->Buffer);
1209                     mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
1210                     pRect++;
1211 
1212                     for( sal_uLong n = 1; n < pHeader.nCount; n++, pRect++ )
1213                     {
1214                         ScopedHRGN hRgn(CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom));
1215                         CombineRgn( mrParent.mhRegion, mrParent.mhRegion, hRgn.get(), RGN_OR );
1216                     }
1217                 }
1218             }
1219 
1220             if ( mrParent.mpClipRgnData != mrParent.mpStdClipRgnData )
1221                 delete [] reinterpret_cast<BYTE*>(mrParent.mpClipRgnData);
1222         }
1223     }
1224 
1225     if( mrParent.mhRegion )
1226     {
1227         SelectClipRgn( mrParent.getHDC(), mrParent.mhRegion );
1228 
1229         // debug code if you want to check range of the newly applied ClipRegion
1230         //RECT aBound;
1231         //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1232     }
1233     else
1234     {
1235         // #i123585# See above, this is a valid case, execute it
1236         SelectClipRgn( mrParent.getHDC(), nullptr );
1237     }
1238 }
1239 
SetLineColor()1240 void WinSalGraphicsImpl::SetLineColor()
1241 {
1242     ResetPen(GetStockPen(NULL_PEN));
1243 
1244     // set new data
1245     mbPen       = false;
1246     mbStockPen  = true;
1247 }
1248 
SetLineColor(Color nColor)1249 void WinSalGraphicsImpl::SetLineColor(Color nColor)
1250 {
1251     COLORREF nPenColor = PALETTERGB(nColor.GetRed(),
1252                                     nColor.GetGreen(),
1253                                     nColor.GetBlue());
1254     bool bStockPen = false;
1255 
1256     HPEN hNewPen = SearchStockPen(nPenColor);
1257     if (hNewPen)
1258         bStockPen = true;
1259     else
1260         hNewPen = MakePen(nColor);
1261 
1262     ResetPen(hNewPen);
1263 
1264     // set new data
1265     mnPenColor  = nPenColor;
1266     maLineColor = nColor;
1267     mbPen       = true;
1268     mbStockPen  = bStockPen;
1269 }
1270 
SearchStockPen(COLORREF nPenColor)1271 HPEN WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor)
1272 {
1273     // Only screen, because printer has problems, when we use stock objects.
1274     if (mrParent.isPrinter())
1275         return nullptr;
1276 
1277     const SalData* pSalData = GetSalData();
1278 
1279     for (sal_uInt16 i = 0; i < pSalData->mnStockPenCount; i++)
1280     {
1281         if (nPenColor == pSalData->maStockPenColorAry[i])
1282             return pSalData->mhStockPenAry[i];
1283     }
1284 
1285     return nullptr;
1286 }
1287 
MakePen(Color nColor)1288 HPEN WinSalGraphicsImpl::MakePen(Color nColor)
1289 {
1290     COLORREF nPenColor = RGB(nColor.GetRed(),
1291                             nColor.GetGreen(),
1292                             nColor.GetBlue());
1293 
1294     return CreatePen(PS_SOLID, mrParent.mnPenWidth, nPenColor);
1295 }
1296 
ResetPen(HPEN hNewPen)1297 void WinSalGraphicsImpl::ResetPen(HPEN hNewPen)
1298 {
1299     HPEN hOldPen = SelectPen(mrParent.getHDC(), hNewPen);
1300 
1301     if (mhPen)
1302     {
1303         if (!mbStockPen)
1304         {
1305             DeletePen(mhPen);
1306         }
1307     }
1308     else
1309     {
1310         mrParent.mhDefPen = hOldPen;
1311     }
1312 
1313     mhPen = hNewPen;
1314 }
1315 
SetFillColor()1316 void WinSalGraphicsImpl::SetFillColor()
1317 {
1318     ResetBrush(GetStockBrush(NULL_BRUSH));
1319 
1320     // set new data
1321     mbBrush     = false;
1322     mbStockBrush = true;
1323 }
1324 
SetFillColor(Color nColor)1325 void WinSalGraphicsImpl::SetFillColor(Color nColor)
1326 {
1327     COLORREF nBrushColor = PALETTERGB(nColor.GetRed(),
1328                                       nColor.GetGreen(),
1329                                       nColor.GetBlue());
1330     bool bStockBrush = false;
1331 
1332     HBRUSH hNewBrush = SearchStockBrush(nBrushColor);
1333     if (hNewBrush)
1334         bStockBrush = true;
1335     else
1336         hNewBrush = MakeBrush(nColor);
1337 
1338     ResetBrush(hNewBrush);
1339 
1340     // set new data
1341     mnBrushColor = nBrushColor;
1342     maFillColor = nColor;
1343     mbBrush     = true;
1344     mbStockBrush = bStockBrush;
1345 }
1346 
SearchStockBrush(COLORREF nBrushColor)1347 HBRUSH WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor)
1348 {
1349     // Only screen, because printer has problems, when we use stock objects.
1350     if (!mrParent.isPrinter())
1351     {
1352         const SalData* pSalData = GetSalData();
1353 
1354         for (sal_uInt16 i = 0; i < pSalData->mnStockBrushCount; i++)
1355         {
1356             if (nBrushColor == pSalData->maStockBrushColorAry[i])
1357                 return pSalData->mhStockBrushAry[i];
1358         }
1359     }
1360 
1361     return nullptr;
1362 }
1363 
MakeBrush(Color nColor)1364 HBRUSH WinSalGraphicsImpl::MakeBrush(Color nColor)
1365 {
1366     const BYTE        nRed        = nColor.GetRed();
1367     const BYTE        nGreen      = nColor.GetGreen();
1368     const BYTE        nBlue       = nColor.GetBlue();
1369     const COLORREF    nBrushColor = RGB(nRed, nGreen, nBlue);
1370 
1371     return CreateSolidBrush(nBrushColor);
1372 }
1373 
ResetBrush(HBRUSH hNewBrush)1374 void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush)
1375 {
1376     HBRUSH hOldBrush = SelectBrush(mrParent.getHDC(), hNewBrush);
1377 
1378     if (mhBrush)
1379     {
1380         if (!mbStockBrush)
1381         {
1382             DeleteBrush(mhBrush);
1383         }
1384     }
1385     else
1386     {
1387         mrParent.mhDefBrush = hOldBrush;
1388     }
1389 
1390     mhBrush = hNewBrush;
1391 }
1392 
SetXORMode(bool bSet,bool)1393 void WinSalGraphicsImpl::SetXORMode( bool bSet, bool )
1394 {
1395     mbXORMode = bSet;
1396     ::SetROP2( mrParent.getHDC(), bSet ? R2_XORPEN : R2_COPYPEN );
1397 }
1398 
SetROPLineColor(SalROPColor nROPColor)1399 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor )
1400 {
1401     SetLineColor( ImplGetROPColor( nROPColor ) );
1402 }
1403 
SetROPFillColor(SalROPColor nROPColor)1404 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor )
1405 {
1406     SetFillColor( ImplGetROPColor( nROPColor ) );
1407 }
1408 
DrawPixelImpl(tools::Long nX,tools::Long nY,COLORREF crColor)1409 void WinSalGraphicsImpl::DrawPixelImpl( tools::Long nX, tools::Long nY, COLORREF crColor )
1410 {
1411     const HDC hDC = mrParent.getHDC();
1412 
1413     if (!mbXORMode)
1414     {
1415         SetPixel(hDC, static_cast<int>(nX), static_cast<int>(nY), crColor);
1416         return;
1417     }
1418 
1419     ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(crColor));
1420     PatBlt(hDC, static_cast<int>(nX), static_cast<int>(nY), int(1), int(1), PATINVERT);
1421 }
1422 
drawPixel(tools::Long nX,tools::Long nY)1423 void WinSalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY )
1424 {
1425     DrawPixelImpl( nX, nY, mnPenColor );
1426 }
1427 
drawPixel(tools::Long nX,tools::Long nY,Color nColor)1428 void WinSalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
1429 {
1430     COLORREF nColorRef = RGB( nColor.GetRed(),
1431                         nColor.GetGreen(),
1432                         nColor.GetBlue() );
1433 
1434     if (nColor.GetAlpha() != 255)
1435     {
1436         const HDC hDC = mrParent.getHDC();
1437 
1438         if (!mbXORMode)
1439         {
1440             // the only way to draw a pixel with alpha is via GDI+
1441             Gdiplus::Graphics g (hDC);
1442             g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy);
1443             Gdiplus::SolidBrush brush(Gdiplus::Color(nColor.GetAlpha(), nColor.GetRed(), nColor.GetGreen(), nColor.GetBlue()));
1444             g.FillRectangle(&brush, static_cast<int>(nX), static_cast<int>(nY), 1, 1);
1445         }
1446         else
1447         {
1448             assert(false && "I am not even sure what it would mean to use alpha in this mode");
1449             ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(nColorRef));
1450             PatBlt(hDC, static_cast<int>(nX), static_cast<int>(nY), int(1), int(1), PATINVERT);
1451         }
1452     }
1453     else
1454     {
1455         const HDC hDC = mrParent.getHDC();
1456 
1457         if (!mbXORMode)
1458         {
1459             SetPixel(hDC, static_cast<int>(nX), static_cast<int>(nY), nColorRef);
1460             return;
1461         }
1462 
1463         ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(nColorRef));
1464         PatBlt(hDC, static_cast<int>(nX), static_cast<int>(nY), int(1), int(1), PATINVERT);
1465     }
1466 }
1467 
drawLine(tools::Long nX1,tools::Long nY1,tools::Long nX2,tools::Long nY2)1468 void WinSalGraphicsImpl::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
1469 {
1470     MoveToEx( mrParent.getHDC(), static_cast<int>(nX1), static_cast<int>(nY1), nullptr );
1471 
1472     LineTo( mrParent.getHDC(), static_cast<int>(nX2), static_cast<int>(nY2) );
1473 
1474     // LineTo doesn't draw the last pixel
1475     if ( !mrParent.isPrinter() )
1476         DrawPixelImpl( nX2, nY2, mnPenColor );
1477 }
1478 
drawRect(tools::Long nX,tools::Long nY,tools::Long nWidth,tools::Long nHeight)1479 void WinSalGraphicsImpl::drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
1480 {
1481     if ( !mbPen )
1482     {
1483         if ( !mrParent.isPrinter() )
1484         {
1485             if (maFillColor.GetAlpha() == 255)
1486             {
1487                 PatBlt( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nWidth), static_cast<int>(nHeight),
1488                         mbXORMode ? PATINVERT : PATCOPY );
1489             }
1490             else if (mbXORMode)
1491                 assert(false && "don't even know what this would mean");
1492             else
1493             {
1494                 // the only way to draw with alpha is via GDI+
1495                 Gdiplus::Graphics g(mrParent.getHDC());
1496                 if (g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy) != Gdiplus::Ok)
1497                     SAL_WARN("vcl", "SetCompositingMode failed");
1498                 Gdiplus::SolidBrush brush(
1499                     Gdiplus::Color(maFillColor.GetAlpha(), maFillColor.GetRed(),
1500                                    maFillColor.GetGreen(), maFillColor.GetBlue()));
1501                 g.FillRectangle(&brush, static_cast<int>(nX), static_cast<int>(nY), nWidth, nHeight);
1502             }
1503         }
1504         else
1505         {
1506             RECT aWinRect;
1507             aWinRect.left   = nX;
1508             aWinRect.top    = nY;
1509             aWinRect.right  = nX+nWidth;
1510             aWinRect.bottom = nY+nHeight;
1511             ::FillRect( mrParent.getHDC(), &aWinRect, mhBrush );
1512         }
1513     }
1514     else
1515     {
1516         if (maFillColor.GetAlpha() == 255)
1517             Rectangle(mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY),
1518                         static_cast<int>(nX + nWidth), static_cast<int>(nY + nHeight));
1519         else if (mbXORMode)
1520             assert(false && "don't even know what this would mean");
1521         else
1522         {
1523             // the only way to draw with alpha is via GDI+
1524             Gdiplus::Graphics g(mrParent.getHDC());
1525             if (g.SetCompositingMode(Gdiplus::CompositingModeSourceCopy) != Gdiplus::Ok)
1526                 SAL_WARN("vcl", "SetCompositingMode failed");
1527             Gdiplus::SolidBrush brush(
1528                 Gdiplus::Color(maFillColor.GetAlpha(), maFillColor.GetRed(),
1529                                 maFillColor.GetGreen(), maFillColor.GetBlue()));
1530             g.FillRectangle(&brush, static_cast<int>(nX), static_cast<int>(nY), nWidth,
1531                             nHeight);
1532         }
1533     }
1534 }
1535 
drawPolyLine(sal_uInt32 nPoints,const Point * pPtAry)1536 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry )
1537 {
1538     std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
1539     for (sal_uInt32 i=0; i<nPoints; ++i)
1540         pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
1541 
1542     Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) );
1543 
1544     // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1545     if ( !mrParent.isPrinter() )
1546         DrawPixelImpl( pWinPtAry[nPoints-1].x, pWinPtAry[nPoints-1].y, mnPenColor );
1547 }
1548 
drawPolygon(sal_uInt32 nPoints,const Point * pPtAry)1549 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
1550 {
1551     std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
1552     for (sal_uInt32 i=0; i<nPoints; ++i)
1553         pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
1554 
1555     Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) );
1556 }
1557 
drawPolyPolygon(sal_uInt32 nPoly,const sal_uInt32 * pPoints,const Point ** pPtAry)1558 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1559                                    const Point** pPtAry )
1560 {
1561     UINT    aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
1562     UINT*   pWinPointAry;
1563     UINT    nPolyPolyPoints = 0;
1564     UINT    nPoints;
1565     UINT    i;
1566 
1567     if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
1568         pWinPointAry = aWinPointAry;
1569     else
1570         pWinPointAry = new UINT[nPoly];
1571 
1572     for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1573     {
1574         nPoints = static_cast<UINT>(pPoints[i])+1;
1575         pWinPointAry[i] = nPoints;
1576         nPolyPolyPoints += nPoints;
1577     }
1578 
1579     POINT  aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
1580     POINT* pWinPointAryAry;
1581     if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
1582         pWinPointAryAry = aWinPointAryAry;
1583     else
1584         pWinPointAryAry = new POINT[nPolyPolyPoints];
1585     UINT            n = 0;
1586     for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1587     {
1588         nPoints = pWinPointAry[i];
1589         const Point* pPolyAry = pPtAry[i];
1590         for (sal_uInt32 j=0; j<nPoints-1; ++j)
1591             pWinPointAryAry[n+j] = POINT { static_cast<LONG>(pPolyAry[j].getX()), static_cast<LONG>(pPolyAry[j].getY()) };
1592         pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
1593         n += nPoints;
1594     }
1595 
1596     PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), static_cast<UINT>(nPoly) );
1597 
1598     if ( pWinPointAry != aWinPointAry )
1599         delete [] pWinPointAry;
1600     if ( pWinPointAryAry != aWinPointAryAry )
1601         delete [] pWinPointAryAry;
1602 }
1603 
drawPolyLineBezier(sal_uInt32 nPoints,const Point * pPtAry,const PolyFlags * pFlgAry)1604 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
1605 {
1606     // #100127# draw an array of points which might also contain bezier control points
1607     if (!nPoints)
1608         return true;
1609 
1610     const HDC hdc = mrParent.getHDC();
1611 
1612     // TODO: profile whether the following options are faster:
1613     // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1614     // b) convert our flag array to window's and use PolyDraw
1615     MoveToEx(hdc, static_cast<LONG>(pPtAry->getX()), static_cast<LONG>(pPtAry->getY()), nullptr);
1616     ++pPtAry;
1617     ++pFlgAry;
1618 
1619     for(sal_uInt32 i = 1; i < nPoints; ++i)
1620     {
1621         if(*pFlgAry != PolyFlags::Control)
1622         {
1623             LineTo(hdc, pPtAry->getX(), pPtAry->getY());
1624         }
1625         else if(nPoints - i > 2)
1626         {
1627             POINT bezierPoints[] = {
1628                 POINT { static_cast<LONG>(pPtAry[0].getX()), static_cast<LONG>(pPtAry[0].getY()) },
1629                 POINT { static_cast<LONG>(pPtAry[1].getX()), static_cast<LONG>(pPtAry[1].getY()) },
1630                 POINT { static_cast<LONG>(pPtAry[2].getX()), static_cast<LONG>(pPtAry[2].getY()) },
1631             };
1632             PolyBezierTo(hdc, bezierPoints, 3);
1633             i += 2;
1634             pPtAry += 2;
1635             pFlgAry += 2;
1636         }
1637 
1638         ++pPtAry;
1639         ++pFlgAry;
1640     }
1641 
1642     return true;
1643 }
1644 
drawPolygonBezier(sal_uInt32 nPoints,const Point * pPtAry,const PolyFlags * pFlgAry)1645 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
1646 {
1647     POINT   aStackAry1[SAL_POLY_STACKBUF];
1648     BYTE    aStackAry2[SAL_POLY_STACKBUF];
1649     POINT*  pWinPointAry;
1650     BYTE*   pWinFlagAry;
1651     if( nPoints > SAL_POLY_STACKBUF )
1652     {
1653         pWinPointAry = new POINT[ nPoints ];
1654         pWinFlagAry = new BYTE[ nPoints ];
1655     }
1656     else
1657     {
1658         pWinPointAry = aStackAry1;
1659         pWinFlagAry = aStackAry2;
1660     }
1661 
1662     sal_uInt32 nPoints_i32(nPoints);
1663     ImplPreparePolyDraw(1, &nPoints_i32, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
1664 
1665     bool bRet( false );
1666 
1667     if( BeginPath( mrParent.getHDC() ) )
1668     {
1669         PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nPoints);
1670 
1671         if( EndPath( mrParent.getHDC() ) )
1672         {
1673             if( StrokeAndFillPath( mrParent.getHDC() ) )
1674                 bRet = true;
1675         }
1676     }
1677 
1678     if( pWinPointAry != aStackAry1 )
1679     {
1680         delete [] pWinPointAry;
1681         delete [] pWinFlagAry;
1682     }
1683 
1684     return bRet;
1685 }
1686 
drawPolyPolygonBezier(sal_uInt32 nPoly,const sal_uInt32 * pPoints,const Point * const * pPtAry,const PolyFlags * const * pFlgAry)1687 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1688                                              const Point* const* pPtAry, const PolyFlags* const* pFlgAry )
1689 {
1690     sal_uLong nCurrPoly, nTotalPoints;
1691     const sal_uInt32* pCurrPoints = pPoints;
1692     for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
1693         nTotalPoints += *pCurrPoints++;
1694 
1695     POINT      aStackAry1[SAL_POLY_STACKBUF];
1696     BYTE       aStackAry2[SAL_POLY_STACKBUF];
1697     POINT*     pWinPointAry;
1698     BYTE*      pWinFlagAry;
1699     if( nTotalPoints > SAL_POLY_STACKBUF )
1700     {
1701         pWinPointAry = new POINT[ nTotalPoints ];
1702         pWinFlagAry = new BYTE[ nTotalPoints ];
1703     }
1704     else
1705     {
1706         pWinPointAry = aStackAry1;
1707         pWinFlagAry = aStackAry2;
1708     }
1709 
1710     ImplPreparePolyDraw(nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
1711 
1712     bool bRet( false );
1713 
1714     if( BeginPath( mrParent.getHDC() ) )
1715     {
1716         PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nTotalPoints);
1717 
1718         if( EndPath( mrParent.getHDC() ) )
1719         {
1720             if( StrokeAndFillPath( mrParent.getHDC() ) )
1721                 bRet = true;
1722         }
1723     }
1724 
1725     if( pWinPointAry != aStackAry1 )
1726     {
1727         delete [] pWinPointAry;
1728         delete [] pWinFlagAry;
1729     }
1730 
1731     return bRet;
1732 }
1733 
impPixelSnap(const basegfx::B2DPolygon & rPolygon,const basegfx::B2DHomMatrix & rObjectToDevice,basegfx::B2DHomMatrix & rObjectToDeviceInv,sal_uInt32 nIndex)1734 static basegfx::B2DPoint impPixelSnap(
1735     const basegfx::B2DPolygon& rPolygon,
1736     const basegfx::B2DHomMatrix& rObjectToDevice,
1737     basegfx::B2DHomMatrix& rObjectToDeviceInv,
1738     sal_uInt32 nIndex)
1739 {
1740     const sal_uInt32 nCount(rPolygon.count());
1741 
1742     // get the data
1743     const basegfx::B2ITuple aPrevTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
1744     const basegfx::B2DPoint aCurrPoint(rObjectToDevice * rPolygon.getB2DPoint(nIndex));
1745     const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
1746     const basegfx::B2ITuple aNextTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
1747 
1748     // get the states
1749     const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
1750     const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
1751     const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
1752     const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
1753     const bool bSnapX(bPrevVertical || bNextVertical);
1754     const bool bSnapY(bPrevHorizontal || bNextHorizontal);
1755 
1756     if(bSnapX || bSnapY)
1757     {
1758         basegfx::B2DPoint aSnappedPoint(
1759             bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
1760             bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
1761 
1762         if(rObjectToDeviceInv.isIdentity())
1763         {
1764             rObjectToDeviceInv = rObjectToDevice;
1765             rObjectToDeviceInv.invert();
1766         }
1767 
1768         aSnappedPoint *= rObjectToDeviceInv;
1769 
1770         return aSnappedPoint;
1771     }
1772 
1773     return rPolygon.getB2DPoint(nIndex);
1774 }
1775 
impAddB2DPolygonToGDIPlusGraphicsPathReal(Gdiplus::GraphicsPath & rGraphicsPath,const basegfx::B2DPolygon & rPolygon,const basegfx::B2DHomMatrix & rObjectToDevice,bool bNoLineJoin,bool bPixelSnapHairline)1776 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1777     Gdiplus::GraphicsPath& rGraphicsPath,
1778     const basegfx::B2DPolygon& rPolygon,
1779     const basegfx::B2DHomMatrix& rObjectToDevice,
1780     bool bNoLineJoin,
1781     bool bPixelSnapHairline)
1782 {
1783     sal_uInt32 nCount(rPolygon.count());
1784     if(nCount == 0)
1785         return;
1786 
1787     const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
1788     if(nEdgeCount == 0)
1789         return;
1790 
1791     const bool bControls(rPolygon.areControlPointsUsed());
1792     basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
1793     basegfx::B2DHomMatrix aObjectToDeviceInv;
1794 
1795     if(bPixelSnapHairline)
1796     {
1797         aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
1798     }
1799 
1800     for(sal_uInt32 a(0); a < nEdgeCount; a++)
1801     {
1802         const sal_uInt32 nNextIndex((a + 1) % nCount);
1803         basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
1804         const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
1805         const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
1806 
1807         if(bPixelSnapHairline)
1808         {
1809             aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
1810         }
1811 
1812         if(b1stControlPointUsed || b2ndControlPointUsed)
1813         {
1814             basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
1815             basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
1816 
1817             // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1818             // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1819             // no 1st or 2nd control point, despite that these are mathematically correct definitions
1820             // (basegfx can handle that).
1821             // Caution: This error (and it's correction) might be necessary for other graphical
1822             // sub-systems in a similar way.
1823             // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1824             // vector was wrong. Best alternative is one as close as possible which means short.
1825             if(!b1stControlPointUsed)
1826             {
1827                 aCa = aCurr + ((aCb - aCurr) * 0.0005);
1828             }
1829             else if(!b2ndControlPointUsed)
1830             {
1831                 aCb = aNext + ((aCa - aNext) * 0.0005);
1832             }
1833 
1834             rGraphicsPath.AddBezier(
1835                 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1836                 static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
1837                 static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
1838                 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1839         }
1840         else
1841         {
1842             rGraphicsPath.AddLine(
1843                 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1844                 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1845         }
1846 
1847         if(a + 1 < nEdgeCount)
1848         {
1849             aCurr = aNext;
1850 
1851             if(bNoLineJoin)
1852             {
1853                 rGraphicsPath.StartFigure();
1854             }
1855         }
1856     }
1857 }
1858 
1859 namespace {
1860 
1861 class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
1862 {
1863 private:
1864     // the path data itself
1865     std::shared_ptr<Gdiplus::GraphicsPath>  mpGraphicsPath;
1866 
1867     // all other values the triangulation is based on and
1868     // need to be compared with to check for data validity
1869     bool                                    mbNoLineJoin;
1870     std::vector< double >                       maStroke;
1871 
1872 public:
1873     SystemDependentData_GraphicsPath(
1874         std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
1875         bool bNoLineJoin,
1876         const std::vector< double >* pStroke); // MM01
1877 
1878     // read access
getGraphicsPath()1879     std::shared_ptr<Gdiplus::GraphicsPath>& getGraphicsPath() { return mpGraphicsPath; }
getNoLineJoin() const1880     bool getNoLineJoin() const { return mbNoLineJoin; }
getStroke() const1881     const std::vector< double >& getStroke() const { return maStroke; }
1882 
1883     virtual sal_Int64 estimateUsageInBytes() const override;
1884 };
1885 
1886 }
1887 
SystemDependentData_GraphicsPath(std::shared_ptr<Gdiplus::GraphicsPath> & rpGraphicsPath,bool bNoLineJoin,const std::vector<double> * pStroke)1888 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
1889     std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
1890     bool bNoLineJoin,
1891     const std::vector< double >* pStroke)
1892 :   basegfx::SystemDependentData(
1893         Application::GetSystemDependentDataManager(),
1894         basegfx::SDD_Type::SDDType_GraphicsPath),
1895     mpGraphicsPath(rpGraphicsPath),
1896     mbNoLineJoin(bNoLineJoin),
1897     maStroke()
1898 {
1899     if(nullptr != pStroke)
1900     {
1901         maStroke = *pStroke;
1902     }
1903 }
1904 
estimateUsageInBytes() const1905 sal_Int64 SystemDependentData_GraphicsPath::estimateUsageInBytes() const
1906 {
1907     sal_Int64 nRetval(0);
1908 
1909     if(!mpGraphicsPath)
1910         return 0;
1911 
1912     const INT nPointCount(mpGraphicsPath->GetPointCount());
1913 
1914     if(0 != nPointCount)
1915     {
1916         // Each point has
1917         // - 2 x sizeof(Gdiplus::REAL)
1918         // - 1 byte (see GetPathTypes in docu)
1919         nRetval = nPointCount * ((2 * sizeof(Gdiplus::REAL)) + 1);
1920     }
1921 
1922     return nRetval;
1923 }
1924 
drawPolyPolygon(const basegfx::B2DHomMatrix & rObjectToDevice,const basegfx::B2DPolyPolygon & rPolyPolygon,double fTransparency)1925 void WinSalGraphicsImpl::drawPolyPolygon(
1926     const basegfx::B2DHomMatrix& rObjectToDevice,
1927     const basegfx::B2DPolyPolygon& rPolyPolygon,
1928     double fTransparency)
1929 {
1930     const sal_uInt32 nCount(rPolyPolygon.count());
1931 
1932     if(!mbBrush || 0 == nCount || fTransparency < 0.0 || fTransparency > 1.0)
1933     {
1934         return;
1935     }
1936 
1937     Gdiplus::Graphics aGraphics(mrParent.getHDC());
1938     const sal_uInt8 nAlpha(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
1939     const Gdiplus::Color aTestColor(nAlpha, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
1940     const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
1941 
1942     // Set full (Object-to-Device) transformation - if used
1943     if(rObjectToDevice.isIdentity())
1944     {
1945         aGraphics.ResetTransform();
1946     }
1947     else
1948     {
1949         Gdiplus::Matrix aMatrix;
1950 
1951         aMatrix.SetElements(
1952             rObjectToDevice.get(0, 0),
1953             rObjectToDevice.get(1, 0),
1954             rObjectToDevice.get(0, 1),
1955             rObjectToDevice.get(1, 1),
1956             rObjectToDevice.get(0, 2),
1957             rObjectToDevice.get(1, 2));
1958         aGraphics.SetTransform(&aMatrix);
1959     }
1960 
1961     // prepare local instance of Gdiplus::GraphicsPath
1962     std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
1963 
1964     // try to access buffered data
1965     std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
1966         rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>(basegfx::SDD_Type::SDDType_GraphicsPath));
1967 
1968     if(pSystemDependentData_GraphicsPath)
1969     {
1970         // copy buffered data
1971         pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
1972     }
1973     else
1974     {
1975         // Note: In principle we could use the same buffered geometry at line
1976         // and fill polygons. Checked that in a first try, used
1977         // GraphicsPath::AddPath from Gdiplus combined with below used
1978         // StartFigure/CloseFigure, worked well (thus the line-draw version
1979         // may create non-closed partial Polygon data).
1980         //
1981         // But in current reality it gets not used due to e.g.
1982         // SdrPathPrimitive2D::create2DDecomposition creating transformed
1983         // line and fill polygon-primitives (what could be changed).
1984         //
1985         // There will probably be more hindrances here in other rendering paths
1986         // which could all be found - intention to do this would be: Use more
1987         // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
1988         //
1989         // A fix for SdrPathPrimitive2D would be to create the sub-geometry
1990         // and embed into a TransformPrimitive2D containing the transformation.
1991         //
1992         // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
1993         // && !bIsHairline) creates polygon fill infos that are not reusable
1994         // for the fill case (see ::drawPolyLine below) - thus we would need a
1995         // bool and/or two system-dependent paths buffered - doable, but complicated.
1996         //
1997         // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
1998         // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
1999         // (at least for now...)
2000 
2001         // create data
2002         pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
2003 
2004         for(sal_uInt32 a(0); a < nCount; a++)
2005         {
2006             if(0 != a)
2007             {
2008                 // #i101491# not needed for first run
2009                 pGraphicsPath->StartFigure();
2010             }
2011 
2012             impAddB2DPolygonToGDIPlusGraphicsPathReal(
2013                 *pGraphicsPath,
2014                 rPolyPolygon.getB2DPolygon(a),
2015                 rObjectToDevice, // not used due to the two 'false' values below, but to not forget later
2016                 false,
2017                 false);
2018 
2019             pGraphicsPath->CloseFigure();
2020         }
2021 
2022         // add to buffering mechanism
2023         rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2024             pGraphicsPath,
2025             false,
2026             nullptr);
2027     }
2028 
2029     if(mrParent.getAntiAlias())
2030     {
2031         aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2032     }
2033     else
2034     {
2035         aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2036     }
2037 
2038     if(mrParent.isPrinter())
2039     {
2040         // #i121591#
2041         // Normally GdiPlus should not be used for printing at all since printers cannot
2042         // print transparent filled polygon geometry and normally this does not happen
2043         // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2044         // and no transparent parts should remain for printing. But this can be overridden
2045         // by the user and thus happens. This call can only come (currently) from
2046         // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2047         // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2048         // checked that there is *no* transformation set and estimated that a stable factor
2049         // dependent of the printer's DPI is used. Create and set a transformation here to
2050         // correct this.
2051         const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2052         const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2053 
2054         // Now the transformation maybe/is already used (see above), so do
2055         // modify it without resetting to not destroy it.
2056         // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2057         // we need - in our notation, would be a multiply from left to execute
2058         // current transform first and this scale last.
2059         // I tried to trigger this code using Print from the menu and various
2060         // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2061         // more, feel free to remove it.
2062         // One more hint: This *may* also be needed now in ::drawPolyLine below
2063         // since it also uses transformations now.
2064         //
2065         // aGraphics.ResetTransform();
2066 
2067         aGraphics.ScaleTransform(
2068             Gdiplus::REAL(100.0) / aDpiX,
2069             Gdiplus::REAL(100.0) / aDpiY,
2070             Gdiplus::MatrixOrderAppend);
2071     }
2072 
2073     // use created or buffered data
2074     aGraphics.FillPath(
2075         &aSolidBrush,
2076         &(*pGraphicsPath));
2077 }
2078 
drawPolyLine(const basegfx::B2DHomMatrix & rObjectToDevice,const basegfx::B2DPolygon & rPolygon,double fTransparency,double fLineWidth,const std::vector<double> * pStroke,basegfx::B2DLineJoin eLineJoin,css::drawing::LineCap eLineCap,double fMiterMinimumAngle,bool bPixelSnapHairline)2079 bool WinSalGraphicsImpl::drawPolyLine(
2080     const basegfx::B2DHomMatrix& rObjectToDevice,
2081     const basegfx::B2DPolygon& rPolygon,
2082     double fTransparency,
2083     double fLineWidth,
2084     const std::vector< double >* pStroke, // MM01
2085     basegfx::B2DLineJoin eLineJoin,
2086     css::drawing::LineCap eLineCap,
2087     double fMiterMinimumAngle,
2088     bool bPixelSnapHairline)
2089 {
2090     // MM01 check done for simple reasons
2091     if(!mbPen || !rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
2092     {
2093         return true;
2094     }
2095 
2096     // need to check/handle LineWidth when ObjectToDevice transformation is used
2097     const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
2098     const bool bIsHairline(fLineWidth == 0);
2099 
2100     // tdf#124848 calculate-back logical LineWidth for a hairline
2101     // since this implementation hands over the transformation to
2102     // the graphic sub-system
2103     if(bIsHairline)
2104     {
2105         fLineWidth = 1.0;
2106 
2107         if(!bObjectToDeviceIsIdentity)
2108         {
2109             basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
2110             aObjectToDeviceInv.invert();
2111             fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
2112         }
2113     }
2114 
2115     Gdiplus::Graphics aGraphics(mrParent.getHDC());
2116     const sal_uInt8 nAlpha = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
2117     const Gdiplus::Color aTestColor(nAlpha, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
2118     Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(fLineWidth));
2119     bool bNoLineJoin(false);
2120 
2121     // Set full (Object-to-Device) transformation - if used
2122     if(bObjectToDeviceIsIdentity)
2123     {
2124         aGraphics.ResetTransform();
2125     }
2126     else
2127     {
2128         Gdiplus::Matrix aMatrix;
2129 
2130         aMatrix.SetElements(
2131             rObjectToDevice.get(0, 0),
2132             rObjectToDevice.get(1, 0),
2133             rObjectToDevice.get(0, 1),
2134             rObjectToDevice.get(1, 1),
2135             rObjectToDevice.get(0, 2),
2136             rObjectToDevice.get(1, 2));
2137         aGraphics.SetTransform(&aMatrix);
2138     }
2139 
2140     switch(eLineJoin)
2141     {
2142         case basegfx::B2DLineJoin::NONE :
2143         {
2144             if(!bIsHairline)
2145             {
2146                 bNoLineJoin = true;
2147             }
2148             break;
2149         }
2150         case basegfx::B2DLineJoin::Bevel :
2151         {
2152             aPen.SetLineJoin(Gdiplus::LineJoinBevel);
2153             break;
2154         }
2155         case basegfx::B2DLineJoin::Miter :
2156         {
2157             const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
2158 
2159             aPen.SetMiterLimit(aMiterLimit);
2160             // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2161             // graphics, somewhere clipped in some distance from the edge point, dependent
2162             // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2163             // that instead
2164             aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
2165             break;
2166         }
2167         case basegfx::B2DLineJoin::Round :
2168         {
2169             aPen.SetLineJoin(Gdiplus::LineJoinRound);
2170             break;
2171         }
2172     }
2173 
2174     switch(eLineCap)
2175     {
2176         default: /*css::drawing::LineCap_BUTT*/
2177         {
2178             // nothing to do
2179             break;
2180         }
2181         case css::drawing::LineCap_ROUND:
2182         {
2183             aPen.SetStartCap(Gdiplus::LineCapRound);
2184             aPen.SetEndCap(Gdiplus::LineCapRound);
2185             break;
2186         }
2187         case css::drawing::LineCap_SQUARE:
2188         {
2189             aPen.SetStartCap(Gdiplus::LineCapSquare);
2190             aPen.SetEndCap(Gdiplus::LineCapSquare);
2191             break;
2192         }
2193     }
2194 
2195     // prepare local instance of Gdiplus::GraphicsPath
2196     std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
2197 
2198     // try to access buffered data
2199     std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
2200         rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>(basegfx::SDD_Type::SDDType_GraphicsPath));
2201 
2202     // MM01 need to do line dashing as fallback stuff here now
2203     const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
2204     const bool bStrokeUsed(0.0 != fDotDashLength);
2205     assert(!bStrokeUsed || (bStrokeUsed && pStroke));
2206 
2207     // MM01 decide if to stroke directly
2208     static bool bDoDirectGDIPlusStroke(true);
2209 
2210     // activate to stroke directly
2211     if(bDoDirectGDIPlusStroke && bStrokeUsed)
2212     {
2213         // tdf#124848 the fix of tdf#130478 that was needed here before
2214         // gets much easier when already handling the hairline case above,
2215         // the back-calculated logical linewidth is already here, just use it.
2216         // Still be careful - a zero LineWidth *should* not happen, but...
2217         std::vector<Gdiplus::REAL> aDashArray(pStroke->size());
2218         const double fFactor(fLineWidth == 0 ? 1.0 : 1.0 / fLineWidth);
2219 
2220         // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
2221         // dash or dot themselves. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
2222         // and other renders (e.g. Impress slide show), while keeping the total length of the
2223         // pattern.
2224         // Patterns are always a sequence dash space dash space ...
2225         if (eLineCap != css::drawing::LineCap_BUTT)
2226         {
2227             size_t nSize = pStroke->size();
2228             // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
2229             // last item.
2230             nSize /= 2;
2231             for(size_t a(0); a < nSize; a++)
2232             {
2233                 double fDashLengthRel = (*pStroke)[2 * a] * fFactor;
2234                 double fSpaceLengthRel = (*pStroke)[2 * a + 1] * fFactor;
2235                 // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
2236                 // appearance is different, in case space is too small.
2237                 double fCorrect = fSpaceLengthRel - 1.0 <= 0 ? fSpaceLengthRel - 0.01 : 1.0;
2238                 aDashArray[2 * a] = Gdiplus::REAL(fDashLengthRel + fCorrect);
2239                 aDashArray[2 * a + 1] = Gdiplus::REAL(fSpaceLengthRel - fCorrect);
2240             }
2241         }
2242         else
2243         {
2244             for(size_t a(0); a < pStroke->size(); a++)
2245             {
2246                 aDashArray[a] = Gdiplus::REAL((*pStroke)[a] * fFactor);
2247             }
2248         }
2249         if (eLineCap == css::drawing::LineCap_ROUND)
2250             aPen.SetDashCap(Gdiplus::DashCapRound);
2251         else
2252             aPen.SetDashCap(Gdiplus::DashCapFlat); // "square" doesn't exist in Gdiplus
2253         aPen.SetDashOffset(Gdiplus::REAL(0.0));
2254         aPen.SetDashPattern(aDashArray.data(), aDashArray.size());
2255     }
2256 
2257     if(!bDoDirectGDIPlusStroke && pSystemDependentData_GraphicsPath)
2258     {
2259         // MM01 - check on stroke change. Used against not used, or if oth used,
2260         // equal or different? Triangulation geometry creation depends heavily
2261         // on stroke, independent of being transformation independent
2262         const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath->getStroke().empty());
2263 
2264         if(bStrokeWasUsed != bStrokeUsed
2265         || (bStrokeUsed && *pStroke != pSystemDependentData_GraphicsPath->getStroke()))
2266         {
2267             // data invalid, forget
2268             pSystemDependentData_GraphicsPath.reset();
2269         }
2270     }
2271 
2272     if(pSystemDependentData_GraphicsPath)
2273     {
2274         // check data validity
2275         if (pSystemDependentData_GraphicsPath->getNoLineJoin() != bNoLineJoin
2276             || bPixelSnapHairline /*tdf#124700*/)
2277         {
2278             // data invalid, forget
2279             pSystemDependentData_GraphicsPath.reset();
2280         }
2281     }
2282 
2283     if(pSystemDependentData_GraphicsPath)
2284     {
2285         // copy buffered data
2286         pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
2287     }
2288     else
2289     {
2290         // fill data of buffered data
2291         pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
2292 
2293         if(!bDoDirectGDIPlusStroke && bStrokeUsed)
2294         {
2295             // MM01 need to do line dashing as fallback stuff here now
2296             basegfx::B2DPolyPolygon aPolyPolygonLine;
2297 
2298             // apply LineStyle
2299             basegfx::utils::applyLineDashing(
2300                 rPolygon, // source
2301                 *pStroke, // pattern
2302                 &aPolyPolygonLine, // target for lines
2303                 nullptr, // target for gaps
2304                 fDotDashLength); // full length if available
2305 
2306             // MM01 checked/verified, ok
2307             for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
2308             {
2309                 const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
2310                 pGraphicsPath->StartFigure();
2311                 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2312                     *pGraphicsPath,
2313                     aPolyLine,
2314                     rObjectToDevice,
2315                     bNoLineJoin,
2316                     bPixelSnapHairline);
2317             }
2318         }
2319         else
2320         {
2321             // no line dashing or direct stroke, just copy
2322             impAddB2DPolygonToGDIPlusGraphicsPathReal(
2323                 *pGraphicsPath,
2324                 rPolygon,
2325                 rObjectToDevice,
2326                 bNoLineJoin,
2327                 bPixelSnapHairline);
2328 
2329             if(rPolygon.isClosed() && !bNoLineJoin)
2330             {
2331                 // #i101491# needed to create the correct line joins
2332                 pGraphicsPath->CloseFigure();
2333             }
2334         }
2335 
2336         // add to buffering mechanism
2337         if (!bPixelSnapHairline /*tdf#124700*/)
2338         {
2339             rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2340                 pGraphicsPath,
2341                 bNoLineJoin,
2342                 pStroke);
2343         }
2344     }
2345 
2346     if(mrParent.getAntiAlias())
2347     {
2348         aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2349     }
2350     else
2351     {
2352         aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2353     }
2354 
2355     if(mrParent.isPrinter())
2356     {
2357         // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2358         // (look for 'One more hint: This *may* also be needed now in'...).
2359         // See comments in same spot above *urgently* before doing changes here,
2360         // these comments are *still fully valid* at this place (!)
2361         const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2362         const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2363 
2364         aGraphics.ScaleTransform(
2365             Gdiplus::REAL(100.0) / aDpiX,
2366             Gdiplus::REAL(100.0) / aDpiY,
2367             Gdiplus::MatrixOrderAppend);
2368     }
2369 
2370     aGraphics.DrawPath(
2371         &aPen,
2372         &(*pGraphicsPath));
2373 
2374     return true;
2375 }
2376 
paintToGdiPlus(Gdiplus::Graphics & rGraphics,const SalTwoRect & rTR,Gdiplus::Bitmap & rBitmap)2377 static void paintToGdiPlus(
2378     Gdiplus::Graphics& rGraphics,
2379     const SalTwoRect& rTR,
2380     Gdiplus::Bitmap& rBitmap)
2381 {
2382     Gdiplus::Rect aDestRect{ INT(rTR.mnDestX), INT(rTR.mnDestY),
2383                              INT(rTR.mnDestWidth), INT(rTR.mnDestHeight) };
2384 
2385     rGraphics.DrawImage(
2386         &rBitmap,
2387         aDestRect,
2388         rTR.mnSrcX, rTR.mnSrcY, rTR.mnSrcWidth, rTR.mnSrcHeight,
2389         Gdiplus::UnitPixel,
2390         nullptr);
2391 }
2392 
setInterpolationMode(Gdiplus::Graphics & rGraphics,tools::Long rSrcWidth,tools::Long rDestWidth,tools::Long rSrcHeight,tools::Long rDestHeight)2393 static void setInterpolationMode(
2394     Gdiplus::Graphics& rGraphics,
2395     tools::Long rSrcWidth,
2396     tools::Long rDestWidth,
2397     tools::Long rSrcHeight,
2398     tools::Long rDestHeight)
2399 {
2400     const bool bSameWidth(rSrcWidth == rDestWidth);
2401     const bool bSameHeight(rSrcHeight == rDestHeight);
2402 
2403     if(bSameWidth && bSameHeight)
2404     {
2405         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
2406     }
2407     else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
2408     {
2409         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2410     }
2411     else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
2412     {
2413         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
2414     }
2415     else
2416     {
2417         rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2418     }
2419 }
2420 
TryDrawBitmapGDIPlus(const SalTwoRect & rTR,const SalBitmap & rSrcBitmap)2421 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
2422 {
2423     if(!rTR.mnSrcWidth || !rTR.mnSrcHeight || !rTR.mnDestWidth || !rTR.mnDestHeight)
2424         return false;
2425 
2426     assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2427 
2428     const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2429     std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
2430 
2431     if(!aARGB)
2432         return false;
2433 
2434     Gdiplus::Graphics aGraphics(mrParent.getHDC());
2435 
2436     setInterpolationMode(
2437         aGraphics,
2438         rTR.mnSrcWidth,
2439         rTR.mnDestWidth,
2440         rTR.mnSrcHeight,
2441         rTR.mnDestHeight);
2442 
2443     paintToGdiPlus(
2444         aGraphics,
2445         rTR,
2446         *aARGB);
2447 
2448     return true;
2449 }
2450 
drawAlphaBitmap(const SalTwoRect & rTR,const SalBitmap & rSrcBitmap,const SalBitmap & rAlphaBmp)2451 bool WinSalGraphicsImpl::drawAlphaBitmap(
2452     const SalTwoRect& rTR,
2453     const SalBitmap& rSrcBitmap,
2454     const SalBitmap& rAlphaBmp)
2455 {
2456     if(!rTR.mnSrcWidth || !rTR.mnSrcHeight || !rTR.mnDestWidth || !rTR.mnDestHeight)
2457         return false;
2458 
2459     // neither GDI nor GDI+ can properly blend to a surface that has alpha
2460     if (GetBitCount() == 32)
2461         return false;
2462 
2463     assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2464     assert(dynamic_cast<const WinSalBitmap*>(&rAlphaBmp));
2465 
2466     const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2467     const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
2468     std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
2469 
2470     if(!aARGB)
2471         return false;
2472 
2473     Gdiplus::Graphics aGraphics(mrParent.getHDC());
2474 
2475     setInterpolationMode(
2476         aGraphics,
2477         rTR.mnSrcWidth,
2478         rTR.mnDestWidth,
2479         rTR.mnSrcHeight,
2480         rTR.mnDestHeight);
2481 
2482     paintToGdiPlus(
2483         aGraphics,
2484         rTR,
2485         *aARGB);
2486 
2487     return true;
2488 }
2489 
drawTransformedBitmap(const basegfx::B2DPoint & rNull,const basegfx::B2DPoint & rX,const basegfx::B2DPoint & rY,const SalBitmap & rSourceBitmap,const SalBitmap * pAlphaBitmap,double fAlpha)2490 bool WinSalGraphicsImpl::drawTransformedBitmap(
2491     const basegfx::B2DPoint& rNull,
2492     const basegfx::B2DPoint& rX,
2493     const basegfx::B2DPoint& rY,
2494     const SalBitmap& rSourceBitmap,
2495     const SalBitmap* pAlphaBitmap,
2496     double fAlpha)
2497 {
2498     assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
2499     assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
2500 
2501     if( fAlpha != 1.0 )
2502         return false;
2503 
2504     const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
2505     const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
2506     std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
2507 
2508     if(!aARGB)
2509         return false;
2510 
2511     const tools::Long nSrcWidth(aARGB->GetWidth());
2512     const tools::Long nSrcHeight(aARGB->GetHeight());
2513 
2514     if(!nSrcWidth || !nSrcHeight)
2515         return true;
2516 
2517     const tools::Long nDestWidth(basegfx::fround<tools::Long>(basegfx::B2DVector(rX - rNull).getLength()));
2518     const tools::Long nDestHeight(basegfx::fround<tools::Long>(basegfx::B2DVector(rY - rNull).getLength()));
2519 
2520     if(!nDestWidth || !nDestHeight)
2521         return true;
2522 
2523     Gdiplus::Graphics aGraphics(mrParent.getHDC());
2524     Gdiplus::PointF aDestPoints[3];
2525     Gdiplus::ImageAttributes aAttributes;
2526 
2527     setInterpolationMode(
2528         aGraphics,
2529         nSrcWidth,
2530         nDestWidth,
2531         nSrcHeight,
2532         nDestHeight);
2533 
2534     // this mode is only capable of drawing the whole bitmap to a parallelogram
2535     aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
2536     aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
2537     aDestPoints[1].X = Gdiplus::REAL(rX.getX());
2538     aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
2539     aDestPoints[2].X = Gdiplus::REAL(rY.getX());
2540     aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
2541 
2542     aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2543 
2544     aGraphics.DrawImage(
2545         aARGB.get(),
2546         aDestPoints,
2547         3,
2548         Gdiplus::REAL(0.0),
2549         Gdiplus::REAL(0.0),
2550         Gdiplus::REAL(nSrcWidth),
2551         Gdiplus::REAL(nSrcHeight),
2552         Gdiplus::UnitPixel,
2553         &aAttributes);
2554 
2555     return true;
2556 }
2557 
hasFastDrawTransformedBitmap() const2558 bool WinSalGraphicsImpl::hasFastDrawTransformedBitmap() const
2559 {
2560     return false;
2561 }
2562 
drawGradient(const tools::PolyPolygon &,const Gradient &)2563 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
2564         const Gradient& /*rGradient*/)
2565 {
2566     return false;
2567 }
2568 
implDrawGradient(basegfx::B2DPolyPolygon const &,SalGradient const &)2569 bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon const & /*rPolyPolygon*/,
2570     SalGradient const & /*rGradient*/)
2571 {
2572     return false;
2573 }
2574 
supportsOperation(OutDevSupportType) const2575 bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType /*eType*/) const
2576 {
2577     return false;
2578 }
2579 
2580 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2581