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