calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / win / gdi / gdiimpl.cxx
blob81d41cf41d33a436b440b8f3e1ed6bbfc58859be
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cstdlib>
23 #include <memory>
24 #include <numeric>
26 #include <svsys.h>
28 #include "gdiimpl.hxx"
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 <win/wincomp.hxx>
38 #include <win/saldata.hxx>
39 #include <win/salgdi.h>
40 #include <win/salbmp.h>
41 #include <win/scoped_gdi.hxx>
42 #include <vcl/BitmapAccessMode.hxx>
43 #include <vcl/BitmapBuffer.hxx>
44 #include <vcl/BitmapPalette.hxx>
45 #include <win/salframe.h>
46 #include <basegfx/matrix/b2dhommatrixtools.hxx>
47 #include <basegfx/utils/systemdependentdata.hxx>
49 #include <win/salids.hrc>
50 #include <ControlCacheKey.hxx>
52 #include <prewin.h>
54 #include <gdiplus.h>
55 #include <gdiplusenums.h>
56 #include <gdipluscolor.h>
58 #include <postwin.h>
60 #define SAL_POLYPOLYCOUNT_STACKBUF 8
61 #define SAL_POLYPOLYPOINTS_STACKBUF 64
63 #define SAL_POLY_STACKBUF 32
65 namespace {
67 // #100127# Fill point and flag memory from array of points which
68 // might also contain bezier control points for the PolyDraw() GDI method
69 // Make sure pWinPointAry and pWinFlagAry are big enough
70 void ImplPreparePolyDraw( bool bCloseFigures,
71 sal_uLong nPoly,
72 const sal_uInt32* pPoints,
73 const Point* const* pPtAry,
74 const PolyFlags* const* pFlgAry,
75 POINT* pWinPointAry,
76 BYTE* pWinFlagAry )
78 sal_uLong nCurrPoly;
79 for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
81 const Point* pCurrPoint = *pPtAry++;
82 const PolyFlags* pCurrFlag = *pFlgAry++;
83 const sal_uInt32 nCurrPoints = *pPoints++;
84 const bool bHaveFlagArray( pCurrFlag );
85 sal_uLong nCurrPoint;
87 if( nCurrPoints )
89 // start figure
90 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
91 pCurrPoint++;
92 *pWinFlagAry++ = PT_MOVETO;
93 ++pCurrFlag;
95 for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
97 // #102067# Check existence of flag array
98 if( bHaveFlagArray &&
99 ( nCurrPoint + 2 ) < nCurrPoints )
101 PolyFlags P4( pCurrFlag[ 2 ] );
103 if( ( PolyFlags::Control == pCurrFlag[ 0 ] ) &&
104 ( PolyFlags::Control == pCurrFlag[ 1 ] ) &&
105 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
107 // control point one
108 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
109 pCurrPoint++;
110 *pWinFlagAry++ = PT_BEZIERTO;
112 // control point two
113 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
114 pCurrPoint++;
115 *pWinFlagAry++ = PT_BEZIERTO;
117 // end point
118 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
119 pCurrPoint++;
120 *pWinFlagAry++ = PT_BEZIERTO;
122 nCurrPoint += 3;
123 pCurrFlag += 3;
124 continue;
128 // regular line point
129 *pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
130 pCurrPoint++;
131 *pWinFlagAry++ = PT_LINETO;
132 ++pCurrFlag;
133 ++nCurrPoint;
136 // end figure?
137 if( bCloseFigures )
138 pWinFlagAry[-1] |= PT_CLOSEFIGURE;
143 Color ImplGetROPColor( SalROPColor nROPColor )
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;
153 bool IsDitherColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
155 constexpr sal_uInt8 DITHER_PAL_DELTA = 51;
157 return !(nRed % DITHER_PAL_DELTA) &&
158 !(nGreen % DITHER_PAL_DELTA) &&
159 !(nBlue % DITHER_PAL_DELTA);
162 bool IsPaletteColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
164 static const PALETTEENTRY aImplSalSysPalEntryAry[] =
166 { 0, 0, 0, 0 },
167 { 0, 0, 0x80, 0 },
168 { 0, 0x80, 0, 0 },
169 { 0, 0x80, 0x80, 0 },
170 { 0x80, 0, 0, 0 },
171 { 0x80, 0, 0x80, 0 },
172 { 0x80, 0x80, 0, 0 },
173 { 0x80, 0x80, 0x80, 0 },
174 { 0xC0, 0xC0, 0xC0, 0 },
175 { 0, 0, 0xFF, 0 },
176 { 0, 0xFF, 0, 0 },
177 { 0, 0xFF, 0xFF, 0 },
178 { 0xFF, 0, 0, 0 },
179 { 0xFF, 0, 0xFF, 0 },
180 { 0xFF, 0xFF, 0, 0 },
181 { 0xFF, 0xFF, 0xFF, 0 }
184 for (const auto& rPalEntry : aImplSalSysPalEntryAry)
186 if(rPalEntry.peRed == nRed &&
187 rPalEntry.peGreen == nGreen &&
188 rPalEntry.peBlue == nBlue)
190 return true;
194 return false;
197 bool IsExtraColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
199 return (nRed == 0) && (nGreen == 184) && (nBlue == 255);
202 bool ImplIsPaletteEntry(BYTE nRed, BYTE nGreen, BYTE nBlue)
204 return IsDitherColor(nRed, nGreen, nBlue) ||
205 IsPaletteColor(nRed, nGreen, nBlue) ||
206 IsExtraColor(nRed, nGreen, nBlue);
209 } // namespace
211 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics& rParent):
212 mrParent(rParent),
213 mbXORMode(false),
214 mbPen(false),
215 mhPen(nullptr),
216 mbStockPen(false),
217 mbBrush(false),
218 mbStockBrush(false),
219 mhBrush(nullptr)
223 WinSalGraphicsImpl::~WinSalGraphicsImpl()
225 if ( mhPen )
227 if ( !mbStockPen )
228 DeletePen( mhPen );
231 if ( mhBrush )
233 if ( !mbStockBrush )
234 DeleteBrush( mhBrush );
238 void WinSalGraphicsImpl::Init()
242 void WinSalGraphicsImpl::freeResources()
246 bool WinSalGraphicsImpl::drawEPS(tools::Long, tools::Long, tools::Long, tools::Long, void*, sal_uInt32)
248 return false;
251 void WinSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
253 HDC hSrcDC;
254 DWORD nRop;
256 if ( pSrcGraphics )
257 hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->getHDC();
258 else
259 hSrcDC = mrParent.getHDC();
261 if ( mbXORMode )
262 nRop = SRCINVERT;
263 else
264 nRop = SRCCOPY;
266 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
267 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
269 BitBlt( mrParent.getHDC(),
270 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
271 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
272 hSrcDC,
273 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
274 nRop );
276 else
278 int nOldStretchMode = SetStretchBltMode( mrParent.getHDC(), STRETCH_DELETESCANS );
279 StretchBlt( mrParent.getHDC(),
280 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
281 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
282 hSrcDC,
283 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
284 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
285 nRop );
286 SetStretchBltMode( mrParent.getHDC(), nOldStretchMode );
290 namespace
293 void MakeInvisibleArea(const RECT& rSrcRect,
294 int nLeft, int nTop, int nRight, int nBottom,
295 HRGN& rhInvalidateRgn)
297 if (!rhInvalidateRgn)
299 rhInvalidateRgn = CreateRectRgnIndirect(&rSrcRect);
302 ScopedHRGN hTempRgn(CreateRectRgn(nLeft, nTop, nRight, nBottom));
303 CombineRgn(rhInvalidateRgn, rhInvalidateRgn, hTempRgn.get(), RGN_DIFF);
306 void ImplCalcOutSideRgn( const RECT& rSrcRect,
307 int nLeft, int nTop, int nRight, int nBottom,
308 HRGN& rhInvalidateRgn )
310 // calculate area outside the visible region
311 if (rSrcRect.left < nLeft)
313 MakeInvisibleArea(rSrcRect, -31999, 0, nLeft, 31999, rhInvalidateRgn);
315 if (rSrcRect.top < nTop)
317 MakeInvisibleArea(rSrcRect, 0, -31999, 31999, nTop, rhInvalidateRgn);
319 if (rSrcRect.right > nRight)
321 MakeInvisibleArea(rSrcRect, nRight, 0, 31999, 31999, rhInvalidateRgn);
323 if (rSrcRect.bottom > nBottom)
325 MakeInvisibleArea(rSrcRect, 0, nBottom, 31999, 31999, rhInvalidateRgn);
329 } // namespace
331 void WinSalGraphicsImpl::copyArea( tools::Long nDestX, tools::Long nDestY,
332 tools::Long nSrcX, tools::Long nSrcY,
333 tools::Long nSrcWidth, tools::Long nSrcHeight,
334 bool bWindowInvalidate )
336 bool bRestoreClipRgn = false;
337 HRGN hOldClipRgn = nullptr;
338 int nOldClipRgnType = ERROR;
339 HRGN hInvalidateRgn = nullptr;
341 // do we have to invalidate also the overlapping regions?
342 if ( bWindowInvalidate && mrParent.isWindow() )
344 // compute and invalidate those parts that were either off-screen or covered by other windows
345 // while performing the above BitBlt
346 // those regions then have to be invalidated as they contain useless/wrong data
347 RECT aSrcRect;
348 RECT aClipRect;
349 RECT aTempRect;
350 RECT aTempRect2;
351 HRGN hTempRgn;
352 HWND hWnd;
354 // restrict srcRect to this window (calc intersection)
355 aSrcRect.left = static_cast<int>(nSrcX);
356 aSrcRect.top = static_cast<int>(nSrcY);
357 aSrcRect.right = aSrcRect.left+static_cast<int>(nSrcWidth);
358 aSrcRect.bottom = aSrcRect.top+static_cast<int>(nSrcHeight);
359 GetClientRect( mrParent.gethWnd(), &aClipRect );
360 if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
362 // transform srcRect to screen coordinates
363 POINT aPt;
364 aPt.x = 0;
365 aPt.y = 0;
366 ClientToScreen( mrParent.gethWnd(), &aPt );
367 aSrcRect.left += aPt.x;
368 aSrcRect.top += aPt.y;
369 aSrcRect.right += aPt.x;
370 aSrcRect.bottom += aPt.y;
371 hInvalidateRgn = nullptr;
373 // compute the parts that are off screen (ie invisible)
374 RECT theScreen;
375 ImplSalGetWorkArea( nullptr, &theScreen, nullptr ); // find the screen area taking multiple monitors into account
376 ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
378 // calculate regions that are covered by other windows
379 HRGN hTempRgn2 = nullptr;
380 HWND hWndTopWindow = mrParent.gethWnd();
381 // Find the TopLevel Window, because only Windows which are in
382 // in the foreground of our TopLevel window must be considered
383 if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
385 RECT aTempRect3 = aSrcRect;
388 hWndTopWindow = ::GetParent( hWndTopWindow );
390 // Test if the Parent clips our window
391 GetClientRect( hWndTopWindow, &aTempRect );
392 POINT aPt2;
393 aPt2.x = 0;
394 aPt2.y = 0;
395 ClientToScreen( hWndTopWindow, &aPt2 );
396 aTempRect.left += aPt2.x;
397 aTempRect.top += aPt2.y;
398 aTempRect.right += aPt2.x;
399 aTempRect.bottom += aPt2.y;
400 IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
402 while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
404 // If one or more Parents clip our window, then we must
405 // calculate the outside area
406 if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
408 ImplCalcOutSideRgn( aSrcRect,
409 aTempRect3.left, aTempRect3.top,
410 aTempRect3.right, aTempRect3.bottom,
411 hInvalidateRgn );
414 // retrieve the top-most (z-order) child window
415 hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
416 while ( hWnd )
418 if ( hWnd == hWndTopWindow )
419 break;
420 if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
422 GetWindowRect( hWnd, &aTempRect );
423 if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
425 // hWnd covers part or all of aSrcRect
426 if ( !hInvalidateRgn )
427 hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
429 // get full bounding box of hWnd
430 hTempRgn = CreateRectRgnIndirect( &aTempRect );
432 // get region of hWnd (the window may be shaped)
433 if ( !hTempRgn2 )
434 hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
435 int nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
436 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
438 // convert window region to screen coordinates
439 OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
440 // and intersect with the window's bounding box
441 CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
443 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
444 CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
445 DeleteRegion( hTempRgn );
448 // retrieve the next window in the z-order, i.e. the window below hwnd
449 hWnd = GetWindow( hWnd, GW_HWNDNEXT );
451 if ( hTempRgn2 )
452 DeleteRegion( hTempRgn2 );
453 if ( hInvalidateRgn )
455 // hInvalidateRgn contains the fully visible parts of the original srcRect
456 hTempRgn = CreateRectRgnIndirect( &aSrcRect );
457 // subtract it from the original rect to get the occluded parts
458 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
459 DeleteRegion( hTempRgn );
461 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
463 // move the occluded parts to the destination pos
464 int nOffX = static_cast<int>(nDestX-nSrcX);
465 int nOffY = static_cast<int>(nDestY-nSrcY);
466 OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
468 // by excluding hInvalidateRgn from the system's clip region
469 // we will prevent bitblt from copying useless data
470 // especially now shadows from overlapping windows will appear (#i36344)
471 hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
472 nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
474 bRestoreClipRgn = true; // indicate changed clipregion and force invalidate
475 ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
481 BitBlt( mrParent.getHDC(),
482 static_cast<int>(nDestX), static_cast<int>(nDestY),
483 static_cast<int>(nSrcWidth), static_cast<int>(nSrcHeight),
484 mrParent.getHDC(),
485 static_cast<int>(nSrcX), static_cast<int>(nSrcY),
486 SRCCOPY );
488 if( bRestoreClipRgn )
490 // restore old clip region
491 if( nOldClipRgnType != ERROR )
492 SelectClipRgn( mrParent.getHDC(), hOldClipRgn);
493 DeleteRegion( hOldClipRgn );
495 // invalidate regions that were not copied
496 bool bInvalidate = true;
498 // Combine Invalidate vcl::Region with existing ClipRegion
499 HRGN hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
500 if ( GetClipRgn( mrParent.getHDC(), hTempRgn ) == 1 )
502 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
503 if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
504 bInvalidate = false;
506 DeleteRegion( hTempRgn );
508 if ( bInvalidate )
510 InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE );
511 // here we only initiate an update if this is the MainThread,
512 // so that there is no deadlock when handling the Paint event,
513 // as the SolarMutex is already held by this Thread
514 SalData* pSalData = GetSalData();
515 DWORD nCurThreadId = GetCurrentThreadId();
516 if ( pSalData->mnAppThreadId == nCurThreadId )
517 UpdateWindow( mrParent.gethWnd() );
520 DeleteRegion( hInvalidateRgn );
525 namespace {
527 void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSalBitmap,
528 bool bPrinter, int nDrawMode )
530 if( hDC )
532 HGLOBAL hDrawDIB;
533 HBITMAP hDrawDDB = rSalBitmap.ImplGethDDB();
534 std::unique_ptr<WinSalBitmap> xTmpSalBmp;
535 bool bPrintDDB = ( bPrinter && hDrawDDB );
537 if( bPrintDDB )
539 xTmpSalBmp.reset(new WinSalBitmap);
540 xTmpSalBmp->Create(rSalBitmap, vcl::bitDepthToPixelFormat(rSalBitmap.GetBitCount()));
541 hDrawDIB = xTmpSalBmp->ImplGethDIB();
543 else
544 hDrawDIB = rSalBitmap.ImplGethDIB();
546 if( hDrawDIB )
548 PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
549 PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
550 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
551 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
553 StretchDIBits( hDC,
554 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
555 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
556 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY),
557 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
558 pBits, pBI, DIB_RGB_COLORS, nDrawMode );
560 GlobalUnlock( hDrawDIB );
561 SetStretchBltMode( hDC, nOldStretchMode );
563 else if( hDrawDDB && !bPrintDDB )
565 ScopedCachedHDC<CACHED_HDC_DRAW> hBmpDC(hDrawDDB);
567 COLORREF nOldBkColor = RGB(0xFF,0xFF,0xFF);
568 COLORREF nOldTextColor = RGB(0,0,0);
569 bool bMono = ( rSalBitmap.GetBitCount() == 1 );
571 if( bMono )
573 COLORREF nBkColor = RGB( 0xFF, 0xFF, 0xFF );
574 COLORREF nTextColor = RGB( 0x00, 0x00, 0x00 );
575 //fdo#33455 handle 1 bit depth pngs with palette entries
576 //to set fore/back colors
577 if (BitmapBuffer* pBitmapBuffer = const_cast<WinSalBitmap&>(rSalBitmap).AcquireBuffer(BitmapAccessMode::Info))
579 const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
580 if (rPalette.GetEntryCount() == 2)
582 Color nCol = rPalette[0];
583 nTextColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
584 nCol = rPalette[1];
585 nBkColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
587 const_cast<WinSalBitmap&>(rSalBitmap).ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Info);
589 nOldBkColor = SetBkColor( hDC, nBkColor );
590 nOldTextColor = ::SetTextColor( hDC, nTextColor );
593 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
594 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
596 BitBlt( hDC,
597 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
598 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
599 hBmpDC.get(),
600 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
601 nDrawMode );
603 else
605 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
607 StretchBlt( hDC,
608 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
609 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
610 hBmpDC.get(),
611 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
612 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
613 nDrawMode );
615 SetStretchBltMode( hDC, nOldStretchMode );
618 if( bMono )
620 SetBkColor( hDC, nOldBkColor );
621 ::SetTextColor( hDC, nOldTextColor );
627 } // namespace
629 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
631 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
633 if(bTryDirectPaint)
635 // only paint direct when no scaling and no MapMode, else the
636 // more expensive conversions may be done for short-time Bitmap/BitmapEx
637 // used for buffering only
638 if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
640 bTryDirectPaint = false;
644 // try to draw using GdiPlus directly
645 if(bTryDirectPaint && TryDrawBitmapGDIPlus(rPosAry, rSalBitmap))
647 return;
650 // fall back old stuff
651 assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
653 ImplDrawBitmap(mrParent.getHDC(), rPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
654 mrParent.isPrinter(),
655 mbXORMode ? SRCINVERT : SRCCOPY );
658 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
659 const SalBitmap& rSSalBitmap,
660 const SalBitmap& rSTransparentBitmap )
662 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
663 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
665 // try to draw using GdiPlus directly
666 if(bTryDirectPaint && drawAlphaBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap))
668 return;
671 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
672 assert(dynamic_cast<const WinSalBitmap*>(&rSTransparentBitmap));
674 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
675 const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
677 SalTwoRect aPosAry = rPosAry;
678 int nDstX = static_cast<int>(aPosAry.mnDestX);
679 int nDstY = static_cast<int>(aPosAry.mnDestY);
680 int nDstWidth = static_cast<int>(aPosAry.mnDestWidth);
681 int nDstHeight = static_cast<int>(aPosAry.mnDestHeight);
682 HDC hDC = mrParent.getHDC();
684 ScopedHBITMAP hMemBitmap;
685 ScopedHBITMAP hMaskBitmap;
687 if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
689 hMemBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
690 hMaskBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
693 ScopedCachedHDC<CACHED_HDC_1> hMemDC(hMemBitmap.get());
694 ScopedCachedHDC<CACHED_HDC_2> hMaskDC(hMaskBitmap.get());
696 aPosAry.mnDestX = aPosAry.mnDestY = 0;
697 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
699 // WIN/WNT seems to have a minor problem mapping the correct color of the
700 // mask to the palette if we draw the DIB directly ==> draw DDB
701 if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
703 WinSalBitmap aTmp;
705 if( aTmp.Create( rTransparentBitmap, &mrParent ) )
706 ImplDrawBitmap( hMaskDC.get(), aPosAry, aTmp, false, SRCCOPY );
708 else
709 ImplDrawBitmap( hMaskDC.get(), aPosAry, rTransparentBitmap, false, SRCCOPY );
711 // now MemDC contains background, MaskDC the transparency mask
713 // #105055# Respect XOR mode
714 if( mbXORMode )
716 ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
717 // now MaskDC contains the bitmap area with black background
718 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCINVERT );
719 // now MemDC contains background XORed bitmap area on top
721 else
723 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCAND );
724 // now MemDC contains background with masked-out bitmap area
725 ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
726 // now MaskDC contains the bitmap area with black background
727 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCPAINT );
728 // now MemDC contains background and bitmap merged together
730 // copy to output DC
731 BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC.get(), 0, 0, SRCCOPY );
734 bool WinSalGraphicsImpl::drawAlphaRect( tools::Long nX, tools::Long nY, tools::Long nWidth,
735 tools::Long nHeight, sal_uInt8 nTransparency )
737 if( mbPen || !mbBrush || mbXORMode )
738 return false; // can only perform solid fills without XOR.
740 ScopedCachedHDC<CACHED_HDC_1> hMemDC(nullptr);
741 SetPixel( hMemDC.get(), int(0), int(0), mnBrushColor );
743 BLENDFUNCTION aFunc = {
744 AC_SRC_OVER,
746 sal::static_int_cast<sal_uInt8>(255 - 255L*nTransparency/100),
750 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
751 // that to dest hdc
752 bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
753 hMemDC.get(), 0,0,1,1,
754 aFunc ) == TRUE;
756 return bRet;
759 void WinSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry,
760 const SalBitmap& rSSalBitmap,
761 Color nMaskColor)
763 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
765 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
767 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
769 SalTwoRect aPosAry = rPosAry;
770 const HDC hDC = mrParent.getHDC();
772 ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(RGB(nMaskColor.GetRed(),
773 nMaskColor.GetGreen(),
774 nMaskColor.GetBlue())));
776 // WIN/WNT seems to have a minor problem mapping the correct color of the
777 // mask to the palette if we draw the DIB directly ==> draw DDB
778 if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
780 WinSalBitmap aTmp;
782 if( aTmp.Create( rSalBitmap, &mrParent ) )
783 ImplDrawBitmap( hDC, aPosAry, aTmp, false, 0x00B8074AUL );
785 else
786 ImplDrawBitmap( hDC, aPosAry, rSalBitmap, false, 0x00B8074AUL );
789 std::shared_ptr<SalBitmap> WinSalGraphicsImpl::getBitmap( tools::Long nX, tools::Long nY, tools::Long nDX, tools::Long nDY )
791 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
793 std::shared_ptr<WinSalBitmap> pSalBitmap;
795 nDX = std::abs( nDX );
796 nDY = std::abs( nDY );
798 HDC hDC = mrParent.getHDC();
799 HBITMAP hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
800 bool bRet;
803 ScopedCachedHDC<CACHED_HDC_1> hBmpDC(hBmpBitmap);
805 bRet = BitBlt(hBmpDC.get(), 0, 0,
806 static_cast<int>(nDX), static_cast<int>(nDY), hDC,
807 static_cast<int>(nX), static_cast<int>(nY), SRCCOPY) ? TRUE : FALSE;
810 if( bRet )
812 pSalBitmap = std::make_shared<WinSalBitmap>();
814 if( !pSalBitmap->Create( hBmpBitmap ) )
816 pSalBitmap.reset();
819 else
821 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
822 DeleteBitmap( hBmpBitmap );
825 return pSalBitmap;
828 Color WinSalGraphicsImpl::getPixel( tools::Long nX, tools::Long nY )
830 COLORREF aWinCol = ::GetPixel( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY) );
832 if ( CLR_INVALID == aWinCol )
833 return Color( 0, 0, 0 );
834 else
835 return Color( GetRValue( aWinCol ),
836 GetGValue( aWinCol ),
837 GetBValue( aWinCol ) );
840 namespace
843 HBRUSH Get50PercentBrush()
845 SalData* pSalData = GetSalData();
846 if ( !pSalData->mh50Brush )
848 if ( !pSalData->mh50Bmp )
849 pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
850 pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
853 return pSalData->mh50Brush;
856 } // namespace
858 void WinSalGraphicsImpl::invert( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight, SalInvert nFlags )
860 if ( nFlags & SalInvert::TrackFrame )
862 HPEN hDotPen = CreatePen( PS_DOT, 0, 0 );
863 HPEN hOldPen = SelectPen( mrParent.getHDC(), hDotPen );
864 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), GetStockBrush( NULL_BRUSH ) );
865 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
867 Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
869 SetROP2( mrParent.getHDC(), nOldROP );
870 SelectPen( mrParent.getHDC(), hOldPen );
871 SelectBrush( mrParent.getHDC(), hOldBrush );
872 DeletePen( hDotPen );
874 else if ( nFlags & SalInvert::N50 )
876 COLORREF nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
877 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), Get50PercentBrush() );
878 PatBlt( mrParent.getHDC(), nX, nY, nWidth, nHeight, PATINVERT );
879 ::SetTextColor( mrParent.getHDC(), nOldTextColor );
880 SelectBrush( mrParent.getHDC(), hOldBrush );
882 else
884 RECT aRect;
885 aRect.left = static_cast<int>(nX);
886 aRect.top = static_cast<int>(nY);
887 aRect.right = static_cast<int>(nX)+nWidth;
888 aRect.bottom = static_cast<int>(nY)+nHeight;
889 ::InvertRect( mrParent.getHDC(), &aRect );
893 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints, const Point* pPtAry, SalInvert nSalFlags )
895 HPEN hPen;
896 HPEN hOldPen;
897 HBRUSH hBrush;
898 HBRUSH hOldBrush = nullptr;
899 COLORREF nOldTextColor RGB(0,0,0);
900 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
902 if ( nSalFlags & SalInvert::TrackFrame )
903 hPen = CreatePen( PS_DOT, 0, 0 );
904 else
907 if ( nSalFlags & SalInvert::N50 )
908 hBrush = Get50PercentBrush();
909 else
910 hBrush = GetStockBrush( BLACK_BRUSH );
912 hPen = GetStockPen( NULL_PEN );
913 nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
914 hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
916 hOldPen = SelectPen( mrParent.getHDC(), hPen );
918 std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
919 for (sal_uInt32 i=0; i<nPoints; ++i)
920 pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
922 // for Windows 95 and its maximum number of points
923 if ( nSalFlags & SalInvert::TrackFrame )
925 if ( !Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
926 Polyline( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
928 else
930 if ( !Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
931 Polygon( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
934 SetROP2( mrParent.getHDC(), nOldROP );
935 SelectPen( mrParent.getHDC(), hOldPen );
937 if ( nSalFlags & SalInvert::TrackFrame )
938 DeletePen( hPen );
939 else
941 ::SetTextColor( mrParent.getHDC(), nOldTextColor );
942 SelectBrush( mrParent.getHDC(), hOldBrush );
946 sal_uInt16 WinSalGraphicsImpl::GetBitCount() const
948 return static_cast<sal_uInt16>(GetDeviceCaps( mrParent.getHDC(), BITSPIXEL ));
951 tools::Long WinSalGraphicsImpl::GetGraphicsWidth() const
953 if( mrParent.gethWnd() && IsWindow( mrParent.gethWnd() ) )
955 WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
956 if( pFrame )
958 if (pFrame->maGeometry.width())
959 return pFrame->maGeometry.width();
960 else
962 // TODO: perhaps not needed, maGeometry should always be up-to-date
963 RECT aRect;
964 GetClientRect( mrParent.gethWnd(), &aRect );
965 return aRect.right;
970 return 0;
973 void WinSalGraphicsImpl::ResetClipRegion()
975 if ( mrParent.mhRegion )
977 DeleteRegion( mrParent.mhRegion );
978 mrParent.mhRegion = nullptr;
981 SelectClipRgn( mrParent.getHDC(), nullptr );
984 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon& rCandidate)
986 if(rCandidate.areControlPointsUsed())
988 return false;
991 const sal_uInt32 nPointCount(rCandidate.count());
993 if(nPointCount < 2)
995 return true;
998 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount + 1 : nPointCount);
999 basegfx::B2DPoint aLast(rCandidate.getB2DPoint(0));
1001 for(sal_uInt32 a(1); a < nEdgeCount; a++)
1003 const sal_uInt32 nNextIndex(a % nPointCount);
1004 const basegfx::B2DPoint aCurrent(rCandidate.getB2DPoint(nNextIndex));
1006 if(!basegfx::fTools::equal(aLast.getX(), aCurrent.getX()) && !basegfx::fTools::equal(aLast.getY(), aCurrent.getY()))
1008 return false;
1011 aLast = aCurrent;
1014 return true;
1017 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon& rCandidate)
1019 if(rCandidate.areControlPointsUsed())
1021 return false;
1024 for(auto const& rPolygon : rCandidate)
1026 if(!containsOnlyHorizontalAndVerticalEdges(rPolygon))
1028 return false;
1032 return true;
1035 bool WinSalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip )
1037 if ( mrParent.mhRegion )
1039 DeleteRegion( mrParent.mhRegion );
1040 mrParent.mhRegion = nullptr;
1043 bool bUsePolygon(i_rClip.HasPolyPolygonOrB2DPolyPolygon());
1044 static bool bTryToAvoidPolygon(true);
1046 // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1047 // and only contains horizontal/vertical edges. In that case, use the fallback
1048 // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1049 // the correct polygon-to-RegionBand transformation.
1050 // Background is that when using the same Rectangle as rectangle or as Polygon
1051 // clip region will lead to different results; the polygon-based one will be
1052 // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1053 // again is because of the polygon-nature and it's classic handling when filling.
1054 // This also means that all cases which use a 'true' polygon-based incarnation of
1055 // a vcl::Region should know what they do - it may lead to repaint errors.
1056 if(bUsePolygon && bTryToAvoidPolygon)
1058 const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1060 if(!aPolyPolygon.areControlPointsUsed())
1062 if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon))
1064 bUsePolygon = false;
1069 if(bUsePolygon)
1071 // #i122149# check the comment above to know that this may lead to potential repaint
1072 // problems. It may be solved (if needed) by scaling the polygon by one in X
1073 // and Y. Currently the workaround to only use it if really unavoidable will
1074 // solve most cases. When someone is really using polygon-based Regions he
1075 // should know what he is doing.
1076 // Added code to do that scaling to check if it works, testing it.
1077 const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1078 const sal_uInt32 nCount(aPolyPolygon.count());
1080 if( nCount )
1082 std::vector< POINT > aPolyPoints;
1083 aPolyPoints.reserve( 1024 );
1084 std::vector< INT > aPolyCounts( nCount, 0 );
1085 basegfx::B2DHomMatrix aExpand;
1086 sal_uInt32 nTargetCount(0);
1087 static bool bExpandByOneInXandY(true);
1089 if(bExpandByOneInXandY)
1091 const basegfx::B2DRange aRangeS(aPolyPolygon.getB2DRange());
1092 const basegfx::B2DRange aRangeT(aRangeS.getMinimum(), aRangeS.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1093 aExpand = basegfx::utils::createSourceRangeTargetRangeTransform(aRangeS, aRangeT);
1096 for(auto const& rPolygon : aPolyPolygon)
1098 const basegfx::B2DPolygon aPoly(
1099 basegfx::utils::adaptiveSubdivideByDistance(
1100 rPolygon,
1101 1));
1102 const sal_uInt32 nPoints(aPoly.count());
1104 // tdf#40863 For CustomShapes there is a hack (see
1105 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1106 // with a single point in top-left and bottom-right corner
1107 // of the BoundRect to be able to determine the correct BoundRect
1108 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1109 // fails with polygons containing a single pixel, so clipping is
1110 // lost. For now, use only polygons with more than two points - the
1111 // ones that may have an area.
1112 // Note: polygons with one point which are curves may have an area,
1113 // but the polygon is already subdivided here, so no need to test
1114 // this.
1115 if(nPoints > 2)
1117 aPolyCounts[nTargetCount] = nPoints;
1118 nTargetCount++;
1120 for( sal_uInt32 b = 0; b < nPoints; b++ )
1122 basegfx::B2DPoint aPt(aPoly.getB2DPoint(b));
1124 if(bExpandByOneInXandY)
1126 aPt = aExpand * aPt;
1129 POINT aPOINT;
1130 // #i122149# do correct rounding
1131 aPOINT.x = basegfx::fround(aPt.getX());
1132 aPOINT.y = basegfx::fround(aPt.getY());
1133 aPolyPoints.push_back( aPOINT );
1138 if(nTargetCount)
1140 mrParent.mhRegion = CreatePolyPolygonRgn( aPolyPoints.data(), aPolyCounts.data(), nTargetCount, ALTERNATE );
1144 else
1146 RectangleVector aRectangles;
1147 i_rClip.GetRegionRectangles(aRectangles);
1149 sal_uLong nRectBufSize = sizeof(RECT)*aRectangles.size();
1150 if ( aRectangles.size() < SAL_CLIPRECT_COUNT )
1152 if ( !mrParent.mpStdClipRgnData )
1153 mrParent.mpStdClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))]);
1154 mrParent.mpClipRgnData = mrParent.mpStdClipRgnData;
1156 else
1157 mrParent.mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
1158 mrParent.mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
1159 mrParent.mpClipRgnData->rdh.iType = RDH_RECTANGLES;
1160 mrParent.mpClipRgnData->rdh.nCount = aRectangles.size();
1161 mrParent.mpClipRgnData->rdh.nRgnSize = nRectBufSize;
1162 RECT* pBoundRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1163 SetRectEmpty( pBoundRect );
1164 RECT* pNextClipRect = reinterpret_cast<RECT*>(&(mrParent.mpClipRgnData->Buffer));
1165 bool bFirstClipRect = true;
1167 for (auto const& rectangle : aRectangles)
1169 const tools::Long nW(rectangle.GetWidth());
1170 const tools::Long nH(rectangle.GetHeight());
1172 if(nW && nH)
1174 const tools::Long nRight(rectangle.Left() + nW);
1175 const tools::Long nBottom(rectangle.Top() + nH);
1177 if(bFirstClipRect)
1179 pBoundRect->left = rectangle.Left();
1180 pBoundRect->top = rectangle.Top();
1181 pBoundRect->right = nRight;
1182 pBoundRect->bottom = nBottom;
1183 bFirstClipRect = false;
1185 else
1187 if(rectangle.Left() < pBoundRect->left)
1189 pBoundRect->left = static_cast<int>(rectangle.Left());
1192 if(rectangle.Top() < pBoundRect->top)
1194 pBoundRect->top = static_cast<int>(rectangle.Top());
1197 if(nRight > pBoundRect->right)
1199 pBoundRect->right = static_cast<int>(nRight);
1202 if(nBottom > pBoundRect->bottom)
1204 pBoundRect->bottom = static_cast<int>(nBottom);
1208 pNextClipRect->left = static_cast<int>(rectangle.Left());
1209 pNextClipRect->top = static_cast<int>(rectangle.Top());
1210 pNextClipRect->right = static_cast<int>(nRight);
1211 pNextClipRect->bottom = static_cast<int>(nBottom);
1212 pNextClipRect++;
1214 else
1216 mrParent.mpClipRgnData->rdh.nCount--;
1217 mrParent.mpClipRgnData->rdh.nRgnSize -= sizeof( RECT );
1221 // create clip region from ClipRgnData
1222 if(0 == mrParent.mpClipRgnData->rdh.nCount)
1224 // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1225 // that contains no polygons or only empty ones (no width/height). This is
1226 // perfectly fine and we are done, except setting it (see end of method)
1228 else if(1 == mrParent.mpClipRgnData->rdh.nCount)
1230 RECT* pRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1231 mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top,
1232 pRect->right, pRect->bottom );
1234 else if(mrParent.mpClipRgnData->rdh.nCount > 1)
1236 sal_uLong nSize = mrParent.mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
1237 mrParent.mhRegion = ExtCreateRegion( nullptr, nSize, mrParent.mpClipRgnData );
1239 // if ExtCreateRegion(...) is not supported
1240 if( !mrParent.mhRegion )
1242 RGNDATAHEADER const & pHeader = mrParent.mpClipRgnData->rdh;
1244 if( pHeader.nCount )
1246 RECT* pRect = reinterpret_cast<RECT*>(mrParent.mpClipRgnData->Buffer);
1247 mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
1248 pRect++;
1250 for( sal_uLong n = 1; n < pHeader.nCount; n++, pRect++ )
1252 ScopedHRGN hRgn(CreateRectRgn(pRect->left, pRect->top, pRect->right, pRect->bottom));
1253 CombineRgn( mrParent.mhRegion, mrParent.mhRegion, hRgn.get(), RGN_OR );
1258 if ( mrParent.mpClipRgnData != mrParent.mpStdClipRgnData )
1259 delete [] reinterpret_cast<BYTE*>(mrParent.mpClipRgnData);
1263 if( mrParent.mhRegion )
1265 SelectClipRgn( mrParent.getHDC(), mrParent.mhRegion );
1267 // debug code if you want to check range of the newly applied ClipRegion
1268 //RECT aBound;
1269 //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1271 //bool bBla = true;
1273 else
1275 // #i123585# See above, this is a valid case, execute it
1276 SelectClipRgn( mrParent.getHDC(), nullptr );
1279 // #i123585# retval no longer dependent of mrParent.mhRegion, see TaskId comments above
1280 return true;
1283 void WinSalGraphicsImpl::SetLineColor()
1285 ResetPen(GetStockPen(NULL_PEN));
1287 // set new data
1288 mbPen = false;
1289 mbStockPen = true;
1292 void WinSalGraphicsImpl::SetLineColor(Color nColor)
1294 COLORREF nPenColor = PALETTERGB(nColor.GetRed(),
1295 nColor.GetGreen(),
1296 nColor.GetBlue());
1297 bool bStockPen = false;
1299 HPEN hNewPen = SearchStockPen(nPenColor);
1300 if (hNewPen)
1301 bStockPen = true;
1302 else
1303 hNewPen = MakePen(nColor);
1305 ResetPen(hNewPen);
1307 // set new data
1308 mnPenColor = nPenColor;
1309 maLineColor = nColor;
1310 mbPen = true;
1311 mbStockPen = bStockPen;
1314 HPEN WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor)
1316 // Only screen, because printer has problems, when we use stock objects.
1317 if (!mrParent.isPrinter())
1319 const SalData* pSalData = GetSalData();
1321 for (sal_uInt16 i = 0; i < pSalData->mnStockPenCount; i++)
1323 if (nPenColor == pSalData->maStockPenColorAry[i])
1324 return pSalData->mhStockPenAry[i];
1328 return nullptr;
1331 HPEN WinSalGraphicsImpl::MakePen(Color nColor)
1333 COLORREF nPenColor = PALETTERGB(nColor.GetRed(),
1334 nColor.GetGreen(),
1335 nColor.GetBlue());
1337 if (!mrParent.isPrinter())
1339 if (GetSalData()->mhDitherPal && ImplIsSysColorEntry(nColor))
1341 nPenColor = PALRGB_TO_RGB(nPenColor);
1345 return CreatePen(PS_SOLID, mrParent.mnPenWidth, nPenColor);
1348 void WinSalGraphicsImpl::ResetPen(HPEN hNewPen)
1350 HPEN hOldPen = SelectPen(mrParent.getHDC(), hNewPen);
1352 if (mhPen)
1354 if (!mbStockPen)
1356 DeletePen(mhPen);
1359 else
1361 mrParent.mhDefPen = hOldPen;
1364 mhPen = hNewPen;
1367 void WinSalGraphicsImpl::SetFillColor()
1369 ResetBrush(GetStockBrush(NULL_BRUSH));
1371 // set new data
1372 mbBrush = false;
1373 mbStockBrush = true;
1376 void WinSalGraphicsImpl::SetFillColor(Color nColor)
1378 COLORREF nBrushColor = PALETTERGB(nColor.GetRed(),
1379 nColor.GetGreen(),
1380 nColor.GetBlue());
1381 bool bStockBrush = false;
1383 HBRUSH hNewBrush = SearchStockBrush(nBrushColor);
1384 if (hNewBrush)
1385 bStockBrush = true;
1386 else
1387 hNewBrush = MakeBrush(nColor);
1389 ResetBrush(hNewBrush);
1391 // set new data
1392 mnBrushColor = nBrushColor;
1393 maFillColor = nColor;
1394 mbBrush = true;
1395 mbStockBrush = bStockBrush;
1398 HBRUSH WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor)
1400 // Only screen, because printer has problems, when we use stock objects.
1401 if (!mrParent.isPrinter())
1403 const SalData* pSalData = GetSalData();
1405 for (sal_uInt16 i = 0; i < pSalData->mnStockBrushCount; i++)
1407 if (nBrushColor == pSalData->maStockBrushColorAry[i])
1408 return pSalData->mhStockBrushAry[i];
1412 return nullptr;
1415 namespace
1418 BYTE GetDitherMappingValue(BYTE nVal, BYTE nThres, const SalData* pSalData)
1420 return (pSalData->mpDitherDiff[nVal] > nThres) ?
1421 pSalData->mpDitherHigh[nVal] : pSalData->mpDitherLow[nVal];
1424 HBRUSH Make16BitDIBPatternBrush(Color nColor)
1426 const SalData* pSalData = GetSalData();
1428 const BYTE nRed = nColor.GetRed();
1429 const BYTE nGreen = nColor.GetGreen();
1430 const BYTE nBlue = nColor.GetBlue();
1432 static const BYTE aOrdDither16Bit[8][8] =
1434 { 0, 6, 1, 7, 0, 6, 1, 7 },
1435 { 4, 2, 5, 3, 4, 2, 5, 3 },
1436 { 1, 7, 0, 6, 1, 7, 0, 6 },
1437 { 5, 3, 4, 2, 5, 3, 4, 2 },
1438 { 0, 6, 1, 7, 0, 6, 1, 7 },
1439 { 4, 2, 5, 3, 4, 2, 5, 3 },
1440 { 1, 7, 0, 6, 1, 7, 0, 6 },
1441 { 5, 3, 4, 2, 5, 3, 4, 2 }
1444 BYTE* pTmp = pSalData->mpDitherDIBData;
1446 for(int nY = 0; nY < 8; ++nY)
1448 for(int nX = 0; nX < 8; ++nX)
1450 const BYTE nThres = aOrdDither16Bit[nY][nX];
1451 *pTmp++ = GetDitherMappingValue(nBlue, nThres, pSalData);
1452 *pTmp++ = GetDitherMappingValue(nGreen, nThres, pSalData);
1453 *pTmp++ = GetDitherMappingValue(nRed, nThres, pSalData);
1457 return CreateDIBPatternBrush(pSalData->mhDitherDIB, DIB_RGB_COLORS);
1460 HBRUSH Make8BitDIBPatternBrush(Color nColor)
1462 const SalData* pSalData = GetSalData();
1464 const BYTE nRed = nColor.GetRed();
1465 const BYTE nGreen = nColor.GetGreen();
1466 const BYTE nBlue = nColor.GetBlue();
1468 static const BYTE aOrdDither8Bit[8][8] =
1470 { 0, 38, 9, 48, 2, 40, 12, 50 },
1471 { 25, 12, 35, 22, 28, 15, 37, 24 },
1472 { 6, 44, 3, 41, 8, 47, 5, 44 },
1473 { 32, 19, 28, 16, 34, 21, 31, 18 },
1474 { 1, 40, 11, 49, 0, 39, 10, 48 },
1475 { 27, 14, 36, 24, 26, 13, 36, 23 },
1476 { 8, 46, 4, 43, 7, 45, 4, 42 },
1477 { 33, 20, 30, 17, 32, 20, 29, 16 }
1480 BYTE* pTmp = pSalData->mpDitherDIBData;
1482 for (int nY = 0; nY < 8; ++nY)
1484 for (int nX = 0; nX < 8; ++nX)
1486 const BYTE nThres = aOrdDither8Bit[nY][nX];
1487 *pTmp = GetDitherMappingValue(nRed, nThres, pSalData) +
1488 GetDitherMappingValue(nGreen, nThres, pSalData) * 6 +
1489 GetDitherMappingValue(nBlue, nThres, pSalData) * 36;
1490 pTmp++;
1494 return CreateDIBPatternBrush(pSalData->mhDitherDIB, DIB_PAL_COLORS);
1497 } // namespace
1499 HBRUSH WinSalGraphicsImpl::MakeBrush(Color nColor)
1501 const SalData* pSalData = GetSalData();
1503 const BYTE nRed = nColor.GetRed();
1504 const BYTE nGreen = nColor.GetGreen();
1505 const BYTE nBlue = nColor.GetBlue();
1506 const COLORREF nBrushColor = PALETTERGB(nRed, nGreen, nBlue);
1508 if (mrParent.isPrinter() || !pSalData->mhDitherDIB)
1509 return CreateSolidBrush(nBrushColor);
1511 if (24 == reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB)->biBitCount)
1512 return Make16BitDIBPatternBrush(nColor);
1514 if (ImplIsSysColorEntry(nColor))
1515 return CreateSolidBrush(PALRGB_TO_RGB(nBrushColor));
1517 if (ImplIsPaletteEntry(nRed, nGreen, nBlue))
1518 return CreateSolidBrush(nBrushColor);
1520 return Make8BitDIBPatternBrush(nColor);
1523 void WinSalGraphicsImpl::ResetBrush(HBRUSH hNewBrush)
1525 HBRUSH hOldBrush = SelectBrush(mrParent.getHDC(), hNewBrush);
1527 if (mhBrush)
1529 if (!mbStockBrush)
1531 DeleteBrush(mhBrush);
1534 else
1536 mrParent.mhDefBrush = hOldBrush;
1539 mhBrush = hNewBrush;
1542 void WinSalGraphicsImpl::SetXORMode( bool bSet, bool )
1544 mbXORMode = bSet;
1545 ::SetROP2( mrParent.getHDC(), bSet ? R2_XORPEN : R2_COPYPEN );
1548 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor )
1550 SetLineColor( ImplGetROPColor( nROPColor ) );
1553 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor )
1555 SetFillColor( ImplGetROPColor( nROPColor ) );
1558 void WinSalGraphicsImpl::DrawPixelImpl( tools::Long nX, tools::Long nY, COLORREF crColor )
1560 const HDC hDC = mrParent.getHDC();
1562 if (!mbXORMode)
1564 SetPixel(hDC, static_cast<int>(nX), static_cast<int>(nY), crColor);
1565 return;
1568 ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(crColor));
1569 PatBlt(hDC, static_cast<int>(nX), static_cast<int>(nY), int(1), int(1), PATINVERT);
1572 void WinSalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY )
1574 DrawPixelImpl( nX, nY, mnPenColor );
1577 void WinSalGraphicsImpl::drawPixel( tools::Long nX, tools::Long nY, Color nColor )
1579 COLORREF nCol = PALETTERGB( nColor.GetRed(),
1580 nColor.GetGreen(),
1581 nColor.GetBlue() );
1583 if ( !mrParent.isPrinter() &&
1584 GetSalData()->mhDitherPal &&
1585 ImplIsSysColorEntry( nColor ) )
1586 nCol = PALRGB_TO_RGB( nCol );
1588 DrawPixelImpl( nX, nY, nCol );
1591 void WinSalGraphicsImpl::drawLine( tools::Long nX1, tools::Long nY1, tools::Long nX2, tools::Long nY2 )
1593 MoveToEx( mrParent.getHDC(), static_cast<int>(nX1), static_cast<int>(nY1), nullptr );
1595 LineTo( mrParent.getHDC(), static_cast<int>(nX2), static_cast<int>(nY2) );
1597 // LineTo doesn't draw the last pixel
1598 if ( !mrParent.isPrinter() )
1599 DrawPixelImpl( nX2, nY2, mnPenColor );
1602 void WinSalGraphicsImpl::drawRect( tools::Long nX, tools::Long nY, tools::Long nWidth, tools::Long nHeight )
1604 if ( !mbPen )
1606 if ( !mrParent.isPrinter() )
1608 PatBlt( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nWidth), static_cast<int>(nHeight),
1609 mbXORMode ? PATINVERT : PATCOPY );
1611 else
1613 RECT aWinRect;
1614 aWinRect.left = nX;
1615 aWinRect.top = nY;
1616 aWinRect.right = nX+nWidth;
1617 aWinRect.bottom = nY+nHeight;
1618 ::FillRect( mrParent.getHDC(), &aWinRect, mhBrush );
1621 else
1622 Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
1625 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const Point* pPtAry )
1627 std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
1628 for (sal_uInt32 i=0; i<nPoints; ++i)
1629 pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
1631 // for Windows 95 and its maximum number of points
1632 if ( !Polyline( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
1633 Polyline( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
1635 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1636 if ( !mrParent.isPrinter() )
1637 DrawPixelImpl( pWinPtAry[nPoints-1].x, pWinPtAry[nPoints-1].y, mnPenColor );
1640 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const Point* pPtAry )
1642 std::unique_ptr<POINT[]> pWinPtAry(new POINT[nPoints]);
1643 for (sal_uInt32 i=0; i<nPoints; ++i)
1644 pWinPtAry[i] = POINT { static_cast<LONG>(pPtAry[i].getX()), static_cast<LONG>(pPtAry[i].getY()) };
1646 // for Windows 95 and its maximum number of points
1647 if ( !Polygon( mrParent.getHDC(), pWinPtAry.get(), static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
1648 Polygon( mrParent.getHDC(), pWinPtAry.get(), MAX_64KSALPOINTS );
1651 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1652 const Point** pPtAry )
1654 UINT aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
1655 UINT* pWinPointAry;
1656 UINT nPolyPolyPoints = 0;
1657 UINT nPoints;
1658 UINT i;
1660 if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
1661 pWinPointAry = aWinPointAry;
1662 else
1663 pWinPointAry = new UINT[nPoly];
1665 for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1667 nPoints = static_cast<UINT>(pPoints[i])+1;
1668 pWinPointAry[i] = nPoints;
1669 nPolyPolyPoints += nPoints;
1672 POINT aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
1673 POINT* pWinPointAryAry;
1674 if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
1675 pWinPointAryAry = aWinPointAryAry;
1676 else
1677 pWinPointAryAry = new POINT[nPolyPolyPoints];
1678 UINT n = 0;
1679 for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1681 nPoints = pWinPointAry[i];
1682 const Point* pPolyAry = pPtAry[i];
1683 for (sal_uInt32 j=0; j<nPoints-1; ++j)
1684 pWinPointAryAry[n+j] = POINT { static_cast<LONG>(pPolyAry[j].getX()), static_cast<LONG>(pPolyAry[j].getY()) };
1685 pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
1686 n += nPoints;
1689 if ( !PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), static_cast<UINT>(nPoly) ) &&
1690 (nPolyPolyPoints > MAX_64KSALPOINTS) )
1692 nPolyPolyPoints = 0;
1693 nPoly = 0;
1696 nPolyPolyPoints += pWinPointAry[static_cast<UINT>(nPoly)];
1697 nPoly++;
1699 while ( nPolyPolyPoints < MAX_64KSALPOINTS );
1700 nPoly--;
1701 if ( pWinPointAry[static_cast<UINT>(nPoly)] > MAX_64KSALPOINTS )
1702 pWinPointAry[static_cast<UINT>(nPoly)] = MAX_64KSALPOINTS;
1703 if ( nPoly == 1 )
1704 Polygon( mrParent.getHDC(), pWinPointAryAry, *pWinPointAry );
1705 else
1706 PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), nPoly );
1709 if ( pWinPointAry != aWinPointAry )
1710 delete [] pWinPointAry;
1711 if ( pWinPointAryAry != aWinPointAryAry )
1712 delete [] pWinPointAryAry;
1715 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
1717 // #100127# draw an array of points which might also contain bezier control points
1718 if (!nPoints)
1719 return true;
1721 const HDC hdc = mrParent.getHDC();
1723 // TODO: profile whether the following options are faster:
1724 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1725 // b) convert our flag array to window's and use PolyDraw
1726 MoveToEx(hdc, static_cast<LONG>(pPtAry->getX()), static_cast<LONG>(pPtAry->getY()), nullptr);
1727 ++pPtAry;
1728 ++pFlgAry;
1730 for(sal_uInt32 i = 1; i < nPoints; ++i)
1732 if(*pFlgAry != PolyFlags::Control)
1734 LineTo(hdc, pPtAry->getX(), pPtAry->getY());
1736 else if(nPoints - i > 2)
1738 POINT bezierPoints[] = {
1739 POINT { static_cast<LONG>(pPtAry[0].getX()), static_cast<LONG>(pPtAry[0].getY()) },
1740 POINT { static_cast<LONG>(pPtAry[1].getX()), static_cast<LONG>(pPtAry[1].getY()) },
1741 POINT { static_cast<LONG>(pPtAry[2].getX()), static_cast<LONG>(pPtAry[2].getY()) },
1743 PolyBezierTo(hdc, bezierPoints, 3);
1744 i += 2;
1745 pPtAry += 2;
1746 pFlgAry += 2;
1749 ++pPtAry;
1750 ++pFlgAry;
1753 return true;
1756 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
1758 POINT aStackAry1[SAL_POLY_STACKBUF];
1759 BYTE aStackAry2[SAL_POLY_STACKBUF];
1760 POINT* pWinPointAry;
1761 BYTE* pWinFlagAry;
1762 if( nPoints > SAL_POLY_STACKBUF )
1764 pWinPointAry = new POINT[ nPoints ];
1765 pWinFlagAry = new BYTE[ nPoints ];
1767 else
1769 pWinPointAry = aStackAry1;
1770 pWinFlagAry = aStackAry2;
1773 sal_uInt32 nPoints_i32(nPoints);
1774 ImplPreparePolyDraw(true, 1, &nPoints_i32, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
1776 bool bRet( false );
1778 if( BeginPath( mrParent.getHDC() ) )
1780 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nPoints);
1782 if( EndPath( mrParent.getHDC() ) )
1784 if( StrokeAndFillPath( mrParent.getHDC() ) )
1785 bRet = true;
1789 if( pWinPointAry != aStackAry1 )
1791 delete [] pWinPointAry;
1792 delete [] pWinFlagAry;
1795 return bRet;
1798 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1799 const Point* const* pPtAry, const PolyFlags* const* pFlgAry )
1801 sal_uLong nCurrPoly, nTotalPoints;
1802 const sal_uInt32* pCurrPoints = pPoints;
1803 for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
1804 nTotalPoints += *pCurrPoints++;
1806 POINT aStackAry1[SAL_POLY_STACKBUF];
1807 BYTE aStackAry2[SAL_POLY_STACKBUF];
1808 POINT* pWinPointAry;
1809 BYTE* pWinFlagAry;
1810 if( nTotalPoints > SAL_POLY_STACKBUF )
1812 pWinPointAry = new POINT[ nTotalPoints ];
1813 pWinFlagAry = new BYTE[ nTotalPoints ];
1815 else
1817 pWinPointAry = aStackAry1;
1818 pWinFlagAry = aStackAry2;
1821 ImplPreparePolyDraw(true, nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
1823 bool bRet( false );
1825 if( BeginPath( mrParent.getHDC() ) )
1827 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nTotalPoints);
1829 if( EndPath( mrParent.getHDC() ) )
1831 if( StrokeAndFillPath( mrParent.getHDC() ) )
1832 bRet = true;
1836 if( pWinPointAry != aStackAry1 )
1838 delete [] pWinPointAry;
1839 delete [] pWinFlagAry;
1842 return bRet;
1845 static basegfx::B2DPoint impPixelSnap(
1846 const basegfx::B2DPolygon& rPolygon,
1847 const basegfx::B2DHomMatrix& rObjectToDevice,
1848 basegfx::B2DHomMatrix& rObjectToDeviceInv,
1849 sal_uInt32 nIndex)
1851 const sal_uInt32 nCount(rPolygon.count());
1853 // get the data
1854 const basegfx::B2ITuple aPrevTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
1855 const basegfx::B2DPoint aCurrPoint(rObjectToDevice * rPolygon.getB2DPoint(nIndex));
1856 const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
1857 const basegfx::B2ITuple aNextTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
1859 // get the states
1860 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
1861 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
1862 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
1863 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
1864 const bool bSnapX(bPrevVertical || bNextVertical);
1865 const bool bSnapY(bPrevHorizontal || bNextHorizontal);
1867 if(bSnapX || bSnapY)
1869 basegfx::B2DPoint aSnappedPoint(
1870 bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
1871 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
1873 if(rObjectToDeviceInv.isIdentity())
1875 rObjectToDeviceInv = rObjectToDevice;
1876 rObjectToDeviceInv.invert();
1879 aSnappedPoint *= rObjectToDeviceInv;
1881 return aSnappedPoint;
1884 return rPolygon.getB2DPoint(nIndex);
1887 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1888 Gdiplus::GraphicsPath& rGraphicsPath,
1889 const basegfx::B2DPolygon& rPolygon,
1890 const basegfx::B2DHomMatrix& rObjectToDevice,
1891 bool bNoLineJoin,
1892 bool bPixelSnapHairline)
1894 sal_uInt32 nCount(rPolygon.count());
1896 if(nCount)
1898 const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
1900 if(nEdgeCount)
1902 const bool bControls(rPolygon.areControlPointsUsed());
1903 basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
1904 basegfx::B2DHomMatrix aObjectToDeviceInv;
1906 if(bPixelSnapHairline)
1908 aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
1911 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1913 const sal_uInt32 nNextIndex((a + 1) % nCount);
1914 basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
1915 const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
1916 const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
1918 if(bPixelSnapHairline)
1920 aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
1923 if(b1stControlPointUsed || b2ndControlPointUsed)
1925 basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
1926 basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
1928 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1929 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1930 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1931 // (basegfx can handle that).
1932 // Caution: This error (and it's correction) might be necessary for other graphical
1933 // sub-systems in a similar way.
1934 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1935 // vector was wrong. Best alternative is one as close as possible which means short.
1936 if(!b1stControlPointUsed)
1938 aCa = aCurr + ((aCb - aCurr) * 0.0005);
1940 else if(!b2ndControlPointUsed)
1942 aCb = aNext + ((aCa - aNext) * 0.0005);
1945 rGraphicsPath.AddBezier(
1946 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1947 static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
1948 static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
1949 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1951 else
1953 rGraphicsPath.AddLine(
1954 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1955 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1958 if(a + 1 < nEdgeCount)
1960 aCurr = aNext;
1962 if(bNoLineJoin)
1964 rGraphicsPath.StartFigure();
1972 namespace {
1974 class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
1976 private:
1977 // the path data itself
1978 std::shared_ptr<Gdiplus::GraphicsPath> mpGraphicsPath;
1980 // all other values the triangulation is based on and
1981 // need to be compared with to check for data validity
1982 bool mbNoLineJoin;
1983 std::vector< double > maStroke;
1985 public:
1986 SystemDependentData_GraphicsPath(
1987 std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
1988 bool bNoLineJoin,
1989 const std::vector< double >* pStroke); // MM01
1991 // read access
1992 std::shared_ptr<Gdiplus::GraphicsPath>& getGraphicsPath() { return mpGraphicsPath; }
1993 bool getNoLineJoin() const { return mbNoLineJoin; }
1994 const std::vector< double >& getStroke() const { return maStroke; }
1996 virtual sal_Int64 estimateUsageInBytes() const override;
2001 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
2002 std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
2003 bool bNoLineJoin,
2004 const std::vector< double >* pStroke)
2005 : basegfx::SystemDependentData(Application::GetSystemDependentDataManager()),
2006 mpGraphicsPath(rpGraphicsPath),
2007 mbNoLineJoin(bNoLineJoin),
2008 maStroke()
2010 if(nullptr != pStroke)
2012 maStroke = *pStroke;
2016 sal_Int64 SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2018 sal_Int64 nRetval(0);
2020 if(mpGraphicsPath)
2022 const INT nPointCount(mpGraphicsPath->GetPointCount());
2024 if(0 != nPointCount)
2026 // Each point has
2027 // - 2 x sizeof(Gdiplus::REAL)
2028 // - 1 byte (see GetPathTypes in docu)
2029 nRetval = nPointCount * ((2 * sizeof(Gdiplus::REAL)) + 1);
2033 return nRetval;
2036 bool WinSalGraphicsImpl::drawPolyPolygon(
2037 const basegfx::B2DHomMatrix& rObjectToDevice,
2038 const basegfx::B2DPolyPolygon& rPolyPolygon,
2039 double fTransparency)
2041 const sal_uInt32 nCount(rPolyPolygon.count());
2043 if(!mbBrush || 0 == nCount || fTransparency < 0.0 || fTransparency > 1.0)
2045 return true;
2048 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2049 const sal_uInt8 aTrans(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
2050 const Gdiplus::Color aTestColor(aTrans, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
2051 const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
2053 // Set full (Object-to-Device) transformation - if used
2054 if(rObjectToDevice.isIdentity())
2056 aGraphics.ResetTransform();
2058 else
2060 Gdiplus::Matrix aMatrix;
2062 aMatrix.SetElements(
2063 rObjectToDevice.get(0, 0),
2064 rObjectToDevice.get(1, 0),
2065 rObjectToDevice.get(0, 1),
2066 rObjectToDevice.get(1, 1),
2067 rObjectToDevice.get(0, 2),
2068 rObjectToDevice.get(1, 2));
2069 aGraphics.SetTransform(&aMatrix);
2072 // prepare local instance of Gdiplus::GraphicsPath
2073 std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
2075 // try to access buffered data
2076 std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
2077 rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
2079 if(pSystemDependentData_GraphicsPath)
2081 // copy buffered data
2082 pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
2084 else
2086 // Note: In principle we could use the same buffered geometry at line
2087 // and fill polygons. Checked that in a first try, used
2088 // GraphicsPath::AddPath from Gdiplus combined with below used
2089 // StartFigure/CloseFigure, worked well (thus the line-draw version
2090 // may create non-closed partial Polygon data).
2092 // But in current reality it gets not used due to e.g.
2093 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2094 // line and fill polygon-primitives (what could be changed).
2096 // There will probably be more hindrances here in other rendering paths
2097 // which could all be found - intention to do this would be: Use more
2098 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2100 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2101 // and embed into a TransformPrimitive2D containing the transformation.
2103 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2104 // && !bIsHairline) creates polygon fill infos that are not reusable
2105 // for the fill case (see ::drawPolyLine below) - thus we would need a
2106 // bool and/or two system-dependent paths buffered - doable, but complicated.
2108 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2109 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2110 // (at least for now...)
2112 // create data
2113 pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
2115 for(sal_uInt32 a(0); a < nCount; a++)
2117 if(0 != a)
2119 // #i101491# not needed for first run
2120 pGraphicsPath->StartFigure();
2123 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2124 *pGraphicsPath,
2125 rPolyPolygon.getB2DPolygon(a),
2126 rObjectToDevice, // not used due to the two 'false' values below, but to not forget later
2127 false,
2128 false);
2130 pGraphicsPath->CloseFigure();
2133 // add to buffering mechanism
2134 rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2135 pGraphicsPath,
2136 false,
2137 nullptr);
2140 if(mrParent.getAntiAlias())
2142 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2144 else
2146 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2149 if(mrParent.isPrinter())
2151 // #i121591#
2152 // Normally GdiPlus should not be used for printing at all since printers cannot
2153 // print transparent filled polygon geometry and normally this does not happen
2154 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2155 // and no transparent parts should remain for printing. But this can be overridden
2156 // by the user and thus happens. This call can only come (currently) from
2157 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2158 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2159 // checked that there is *no* transformation set and estimated that a stable factor
2160 // dependent of the printer's DPI is used. Create and set a transformation here to
2161 // correct this.
2162 const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2163 const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2165 // Now the transformation maybe/is already used (see above), so do
2166 // modify it without resetting to not destroy it.
2167 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2168 // we need - in our notation, would be a multiply from left to execute
2169 // current transform first and this scale last.
2170 // I tried to trigger this code using Print from the menu and various
2171 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2172 // more, feel free to remove it.
2173 // One more hint: This *may* also be needed now in ::drawPolyLine below
2174 // since it also uses transformations now.
2176 // aGraphics.ResetTransform();
2178 aGraphics.ScaleTransform(
2179 Gdiplus::REAL(100.0) / aDpiX,
2180 Gdiplus::REAL(100.0) / aDpiY,
2181 Gdiplus::MatrixOrderAppend);
2184 // use created or buffered data
2185 aGraphics.FillPath(
2186 &aSolidBrush,
2187 &(*pGraphicsPath));
2189 return true;
2192 bool WinSalGraphicsImpl::drawPolyLine(
2193 const basegfx::B2DHomMatrix& rObjectToDevice,
2194 const basegfx::B2DPolygon& rPolygon,
2195 double fTransparency,
2196 double fLineWidth,
2197 const std::vector< double >* pStroke, // MM01
2198 basegfx::B2DLineJoin eLineJoin,
2199 css::drawing::LineCap eLineCap,
2200 double fMiterMinimumAngle,
2201 bool bPixelSnapHairline)
2203 // MM01 check done for simple reasons
2204 if(!mbPen || !rPolygon.count() || fTransparency < 0.0 || fTransparency > 1.0)
2206 return true;
2209 // need to check/handle LineWidth when ObjectToDevice transformation is used
2210 const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity());
2211 const bool bIsHairline(fLineWidth == 0);
2213 // tdf#124848 calculate-back logical LineWidth for a hairline
2214 // since this implementation hands over the transformation to
2215 // the graphic sub-system
2216 if(bIsHairline)
2218 fLineWidth = 1.0;
2220 if(!bObjectToDeviceIsIdentity)
2222 basegfx::B2DHomMatrix aObjectToDeviceInv(rObjectToDevice);
2223 aObjectToDeviceInv.invert();
2224 fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength();
2228 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2229 const sal_uInt8 aTrans = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
2230 const Gdiplus::Color aTestColor(aTrans, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
2231 Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(fLineWidth));
2232 bool bNoLineJoin(false);
2234 // Set full (Object-to-Device) transformation - if used
2235 if(bObjectToDeviceIsIdentity)
2237 aGraphics.ResetTransform();
2239 else
2241 Gdiplus::Matrix aMatrix;
2243 aMatrix.SetElements(
2244 rObjectToDevice.get(0, 0),
2245 rObjectToDevice.get(1, 0),
2246 rObjectToDevice.get(0, 1),
2247 rObjectToDevice.get(1, 1),
2248 rObjectToDevice.get(0, 2),
2249 rObjectToDevice.get(1, 2));
2250 aGraphics.SetTransform(&aMatrix);
2253 switch(eLineJoin)
2255 case basegfx::B2DLineJoin::NONE :
2257 if(!bIsHairline)
2259 bNoLineJoin = true;
2261 break;
2263 case basegfx::B2DLineJoin::Bevel :
2265 aPen.SetLineJoin(Gdiplus::LineJoinBevel);
2266 break;
2268 case basegfx::B2DLineJoin::Miter :
2270 const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
2272 aPen.SetMiterLimit(aMiterLimit);
2273 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2274 // graphics, somewhere clipped in some distance from the edge point, dependent
2275 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2276 // that instead
2277 aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
2278 break;
2280 case basegfx::B2DLineJoin::Round :
2282 aPen.SetLineJoin(Gdiplus::LineJoinRound);
2283 break;
2287 switch(eLineCap)
2289 default: /*css::drawing::LineCap_BUTT*/
2291 // nothing to do
2292 break;
2294 case css::drawing::LineCap_ROUND:
2296 aPen.SetStartCap(Gdiplus::LineCapRound);
2297 aPen.SetEndCap(Gdiplus::LineCapRound);
2298 break;
2300 case css::drawing::LineCap_SQUARE:
2302 aPen.SetStartCap(Gdiplus::LineCapSquare);
2303 aPen.SetEndCap(Gdiplus::LineCapSquare);
2304 break;
2308 // prepare local instance of Gdiplus::GraphicsPath
2309 std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
2311 // try to access buffered data
2312 std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
2313 rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
2315 // MM01 need to do line dashing as fallback stuff here now
2316 const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0);
2317 const bool bStrokeUsed(0.0 != fDotDashLength);
2318 assert(!bStrokeUsed || (bStrokeUsed && pStroke));
2320 // MM01 decide if to stroke directly
2321 static bool bDoDirectGDIPlusStroke(true);
2323 // activate to stroke directly
2324 if(bDoDirectGDIPlusStroke && bStrokeUsed)
2326 // tdf#124848 the fix of tdf#130478 that was needed here before
2327 // gets much easier when already handling the hairline case above,
2328 // the back-calculated logical linewidth is already here, just use it.
2329 // Still be careful - a zero LineWidth *should* not happen, but...
2330 std::vector<Gdiplus::REAL> aDashArray(pStroke->size());
2331 const double fFactor(fLineWidth == 0 ? 1.0 : 1.0 / fLineWidth);
2333 // tdf#134128. ODF adds caps to the dashes and dots, but GDI makes caps from the
2334 // dash or dot themselves. We tweak aDashArray to look the same in GDI (e.g. Impress edit mode)
2335 // and other renders (e.g. Impress slide show), while keeping the total length of the
2336 // pattern.
2337 // Patterns are always a sequence dash space dash space ...
2338 if (eLineCap != css::drawing::LineCap_BUTT)
2340 size_t nSize = pStroke->size();
2341 // We want to treat dash and space in pairs. There should be no odd size. If so, we ignore
2342 // last item.
2343 nSize /= 2;
2344 for(size_t a(0); a < nSize; a++)
2346 double fDashLengthRel = (*pStroke)[2 * a] * fFactor;
2347 double fSpaceLengthRel = (*pStroke)[2 * a + 1] * fFactor;
2348 // GDI allows only positive lengths for space, Skia negative lengths too. Thus the
2349 // appearance is different, in case space is too small.
2350 double fCorrect = fSpaceLengthRel - 1.0 <= 0 ? fSpaceLengthRel - 0.01 : 1.0;
2351 aDashArray[2 * a] = Gdiplus::REAL(fDashLengthRel + fCorrect);
2352 aDashArray[2 * a + 1] = Gdiplus::REAL(fSpaceLengthRel - fCorrect);
2355 else
2357 for(size_t a(0); a < pStroke->size(); a++)
2359 aDashArray[a] = Gdiplus::REAL((*pStroke)[a] * fFactor);
2362 if (eLineCap == css::drawing::LineCap_ROUND)
2363 aPen.SetDashCap(Gdiplus::DashCapRound);
2364 else
2365 aPen.SetDashCap(Gdiplus::DashCapFlat); // "square" doesn't exist in Gdiplus
2366 aPen.SetDashOffset(Gdiplus::REAL(0.0));
2367 aPen.SetDashPattern(aDashArray.data(), aDashArray.size());
2370 if(!bDoDirectGDIPlusStroke && pSystemDependentData_GraphicsPath)
2372 // MM01 - check on stroke change. Used against not used, or if oth used,
2373 // equal or different? Triangulation geometry creation depends heavily
2374 // on stroke, independent of being transformation independent
2375 const bool bStrokeWasUsed(!pSystemDependentData_GraphicsPath->getStroke().empty());
2377 if(bStrokeWasUsed != bStrokeUsed
2378 || (bStrokeUsed && *pStroke != pSystemDependentData_GraphicsPath->getStroke()))
2380 // data invalid, forget
2381 pSystemDependentData_GraphicsPath.reset();
2385 if(pSystemDependentData_GraphicsPath)
2387 // check data validity
2388 if (pSystemDependentData_GraphicsPath->getNoLineJoin() != bNoLineJoin
2389 || bPixelSnapHairline /*tdf#124700*/)
2391 // data invalid, forget
2392 pSystemDependentData_GraphicsPath.reset();
2396 if(pSystemDependentData_GraphicsPath)
2398 // copy buffered data
2399 pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
2401 else
2403 // fill data of buffered data
2404 pGraphicsPath = std::make_shared<Gdiplus::GraphicsPath>();
2406 if(!bDoDirectGDIPlusStroke && bStrokeUsed)
2408 // MM01 need to do line dashing as fallback stuff here now
2409 basegfx::B2DPolyPolygon aPolyPolygonLine;
2411 // apply LineStyle
2412 basegfx::utils::applyLineDashing(
2413 rPolygon, // source
2414 *pStroke, // pattern
2415 &aPolyPolygonLine, // target for lines
2416 nullptr, // target for gaps
2417 fDotDashLength); // full length if available
2419 // MM01 checked/verified, ok
2420 for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++)
2422 const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a));
2423 pGraphicsPath->StartFigure();
2424 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2425 *pGraphicsPath,
2426 aPolyLine,
2427 rObjectToDevice,
2428 bNoLineJoin,
2429 bPixelSnapHairline);
2432 else
2434 // no line dashing or direct stroke, just copy
2435 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2436 *pGraphicsPath,
2437 rPolygon,
2438 rObjectToDevice,
2439 bNoLineJoin,
2440 bPixelSnapHairline);
2442 if(rPolygon.isClosed() && !bNoLineJoin)
2444 // #i101491# needed to create the correct line joins
2445 pGraphicsPath->CloseFigure();
2449 // add to buffering mechanism
2450 if (!bPixelSnapHairline /*tdf#124700*/)
2452 rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2453 pGraphicsPath,
2454 bNoLineJoin,
2455 pStroke);
2459 if(mrParent.getAntiAlias())
2461 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2463 else
2465 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2468 if(mrParent.isPrinter())
2470 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2471 // (look for 'One more hint: This *may* also be needed now in'...).
2472 // See comments in same spot above *urgently* before doing changes here,
2473 // these comments are *still fully valid* at this place (!)
2474 const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2475 const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2477 aGraphics.ScaleTransform(
2478 Gdiplus::REAL(100.0) / aDpiX,
2479 Gdiplus::REAL(100.0) / aDpiY,
2480 Gdiplus::MatrixOrderAppend);
2483 aGraphics.DrawPath(
2484 &aPen,
2485 &(*pGraphicsPath));
2487 return true;
2490 static void paintToGdiPlus(
2491 Gdiplus::Graphics& rGraphics,
2492 const SalTwoRect& rTR,
2493 Gdiplus::Bitmap& rBitmap)
2495 // only parts of source are used
2496 Gdiplus::PointF aDestPoints[3];
2497 Gdiplus::ImageAttributes aAttributes;
2499 // define target region as parallelogram
2500 aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
2501 aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
2502 aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
2503 aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
2504 aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
2505 aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
2507 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2509 rGraphics.DrawImage(
2510 &rBitmap,
2511 aDestPoints,
2513 Gdiplus::REAL(rTR.mnSrcX),
2514 Gdiplus::REAL(rTR.mnSrcY),
2515 Gdiplus::REAL(rTR.mnSrcWidth),
2516 Gdiplus::REAL(rTR.mnSrcHeight),
2517 Gdiplus::UnitPixel,
2518 &aAttributes);
2521 static void setInterpolationMode(
2522 Gdiplus::Graphics& rGraphics,
2523 tools::Long rSrcWidth,
2524 tools::Long rDestWidth,
2525 tools::Long rSrcHeight,
2526 tools::Long rDestHeight)
2528 const bool bSameWidth(rSrcWidth == rDestWidth);
2529 const bool bSameHeight(rSrcHeight == rDestHeight);
2531 if(bSameWidth && bSameHeight)
2533 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
2535 else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
2537 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2539 else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
2541 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
2543 else
2545 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2549 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
2551 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2553 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2555 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2556 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
2558 if(aARGB)
2560 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2562 setInterpolationMode(
2563 aGraphics,
2564 rTR.mnSrcWidth,
2565 rTR.mnDestWidth,
2566 rTR.mnSrcHeight,
2567 rTR.mnDestHeight);
2569 paintToGdiPlus(
2570 aGraphics,
2571 rTR,
2572 *aARGB);
2574 return true;
2578 return false;
2581 bool WinSalGraphicsImpl::blendBitmap(
2582 const SalTwoRect&,
2583 const SalBitmap&)
2585 return false;
2588 bool WinSalGraphicsImpl::blendAlphaBitmap(
2589 const SalTwoRect&,
2590 const SalBitmap&,
2591 const SalBitmap&,
2592 const SalBitmap&)
2594 return false;
2597 bool WinSalGraphicsImpl::drawAlphaBitmap(
2598 const SalTwoRect& rTR,
2599 const SalBitmap& rSrcBitmap,
2600 const SalBitmap& rAlphaBmp)
2602 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2604 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2605 assert(dynamic_cast<const WinSalBitmap*>(&rAlphaBmp));
2607 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2608 const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
2609 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
2611 if(aARGB)
2613 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2615 setInterpolationMode(
2616 aGraphics,
2617 rTR.mnSrcWidth,
2618 rTR.mnDestWidth,
2619 rTR.mnSrcHeight,
2620 rTR.mnDestHeight);
2622 paintToGdiPlus(
2623 aGraphics,
2624 rTR,
2625 *aARGB);
2627 return true;
2631 return false;
2634 bool WinSalGraphicsImpl::drawTransformedBitmap(
2635 const basegfx::B2DPoint& rNull,
2636 const basegfx::B2DPoint& rX,
2637 const basegfx::B2DPoint& rY,
2638 const SalBitmap& rSourceBitmap,
2639 const SalBitmap* pAlphaBitmap,
2640 double fAlpha)
2642 assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
2643 assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
2645 if( fAlpha != 1.0 )
2646 return false;
2648 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
2649 const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
2650 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
2652 if(aARGB)
2654 const tools::Long nSrcWidth(aARGB->GetWidth());
2655 const tools::Long nSrcHeight(aARGB->GetHeight());
2657 if(nSrcWidth && nSrcHeight)
2659 const tools::Long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
2660 const tools::Long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
2662 if(nDestWidth && nDestHeight)
2664 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2665 Gdiplus::PointF aDestPoints[3];
2666 Gdiplus::ImageAttributes aAttributes;
2668 setInterpolationMode(
2669 aGraphics,
2670 nSrcWidth,
2671 nDestWidth,
2672 nSrcHeight,
2673 nDestHeight);
2675 // this mode is only capable of drawing the whole bitmap to a parallelogram
2676 aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
2677 aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
2678 aDestPoints[1].X = Gdiplus::REAL(rX.getX());
2679 aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
2680 aDestPoints[2].X = Gdiplus::REAL(rY.getX());
2681 aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
2683 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2685 aGraphics.DrawImage(
2686 aARGB.get(),
2687 aDestPoints,
2689 Gdiplus::REAL(0.0),
2690 Gdiplus::REAL(0.0),
2691 Gdiplus::REAL(nSrcWidth),
2692 Gdiplus::REAL(nSrcHeight),
2693 Gdiplus::UnitPixel,
2694 &aAttributes);
2698 return true;
2701 return false;
2704 bool WinSalGraphicsImpl::hasFastDrawTransformedBitmap() const
2706 return false;
2709 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
2710 const Gradient& /*rGradient*/)
2712 return false;
2715 bool WinSalGraphicsImpl::implDrawGradient(basegfx::B2DPolyPolygon const & /*rPolyPolygon*/,
2716 SalGradient const & /*rGradient*/)
2718 return false;
2721 bool WinSalGraphicsImpl::supportsOperation(OutDevSupportType eType) const
2723 static bool bAllowForTest(true);
2724 bool bRet = false;
2726 switch (eType)
2728 case OutDevSupportType::TransparentRect:
2729 bRet = mrParent.mbVirDev || mrParent.mbWindow;
2730 break;
2731 case OutDevSupportType::B2DDraw:
2732 bRet = bAllowForTest;
2733 break;
2734 default:
2735 break;
2737 return bRet;
2740 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */