bump product version to 6.4.0.3
[LibreOffice.git] / vcl / win / gdi / gdiimpl.cxx
blob95fd346af1a515af6a397523e3649aeca8b7032d
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 <svsys.h>
22 #include "gdiimpl.hxx"
24 #include <string.h>
25 #include <rtl/strbuf.hxx>
26 #include <sal/log.hxx>
27 #include <tools/poly.hxx>
28 #include <basegfx/polygon/b2dpolygon.hxx>
29 #include <basegfx/polygon/b2dpolygontools.hxx>
30 #include <basegfx/polygon/b2dpolypolygontools.hxx>
31 #include <win/wincomp.hxx>
32 #include <win/saldata.hxx>
33 #include <win/salgdi.h>
34 #include <win/salbmp.h>
35 #include <win/scoped_gdi.hxx>
36 #include <vcl/BitmapAccessMode.hxx>
37 #include <vcl/BitmapBuffer.hxx>
38 #include <vcl/BitmapPalette.hxx>
39 #include <win/salframe.h>
40 #include <basegfx/matrix/b2dhommatrixtools.hxx>
41 #include <basegfx/utils/systemdependentdata.hxx>
43 #include <outdata.hxx>
44 #include <win/salids.hrc>
45 #include <ControlCacheKey.hxx>
47 #if defined _MSC_VER
48 #ifndef min
49 #define min(a,b) (((a) < (b)) ? (a) : (b))
50 #endif
51 #ifndef max
52 #define max(a,b) (((a) > (b)) ? (a) : (b))
53 #endif
54 #endif
56 #include <prewin.h>
58 #include <gdiplus.h>
59 #include <gdiplusenums.h>
60 #include <gdipluscolor.h>
62 #include <postwin.h>
64 #define SAL_POLYPOLYCOUNT_STACKBUF 8
65 #define SAL_POLYPOLYPOINTS_STACKBUF 64
67 #define SAL_POLY_STACKBUF 32
69 namespace {
71 // #100127# Fill point and flag memory from array of points which
72 // might also contain bezier control points for the PolyDraw() GDI method
73 // Make sure pWinPointAry and pWinFlagAry are big enough
74 void ImplPreparePolyDraw( bool bCloseFigures,
75 sal_uLong nPoly,
76 const sal_uInt32* pPoints,
77 const SalPoint* const* pPtAry,
78 const PolyFlags* const* pFlgAry,
79 POINT* pWinPointAry,
80 BYTE* pWinFlagAry )
82 sal_uLong nCurrPoly;
83 for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
85 const POINT* pCurrPoint = reinterpret_cast<const POINT*>( *pPtAry++ );
86 const PolyFlags* pCurrFlag = *pFlgAry++;
87 const sal_uInt32 nCurrPoints = *pPoints++;
88 const bool bHaveFlagArray( pCurrFlag );
89 sal_uLong nCurrPoint;
91 if( nCurrPoints )
93 // start figure
94 *pWinPointAry++ = *pCurrPoint++;
95 *pWinFlagAry++ = PT_MOVETO;
96 ++pCurrFlag;
98 for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
100 // #102067# Check existence of flag array
101 if( bHaveFlagArray &&
102 ( nCurrPoint + 2 ) < nCurrPoints )
104 PolyFlags P4( pCurrFlag[ 2 ] );
106 if( ( PolyFlags::Control == pCurrFlag[ 0 ] ) &&
107 ( PolyFlags::Control == pCurrFlag[ 1 ] ) &&
108 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
110 // control point one
111 *pWinPointAry++ = *pCurrPoint++;
112 *pWinFlagAry++ = PT_BEZIERTO;
114 // control point two
115 *pWinPointAry++ = *pCurrPoint++;
116 *pWinFlagAry++ = PT_BEZIERTO;
118 // end point
119 *pWinPointAry++ = *pCurrPoint++;
120 *pWinFlagAry++ = PT_BEZIERTO;
122 nCurrPoint += 3;
123 pCurrFlag += 3;
124 continue;
128 // regular line point
129 *pWinPointAry++ = *pCurrPoint++;
130 *pWinFlagAry++ = PT_LINETO;
131 ++pCurrFlag;
132 ++nCurrPoint;
135 // end figure?
136 if( bCloseFigures )
137 pWinFlagAry[-1] |= PT_CLOSEFIGURE;
142 Color ImplGetROPColor( SalROPColor nROPColor )
144 Color nColor;
145 if ( nROPColor == SalROPColor::N0 )
146 nColor = Color( 0, 0, 0 );
147 else
148 nColor = Color( 255, 255, 255 );
149 return nColor;
152 bool IsDitherColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
154 constexpr sal_uInt8 DITHER_PAL_DELTA = 51;
156 return !(nRed % DITHER_PAL_DELTA) &&
157 !(nGreen % DITHER_PAL_DELTA) &&
158 !(nBlue % DITHER_PAL_DELTA);
161 bool IsPaletteColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
163 static const PALETTEENTRY aImplSalSysPalEntryAry[] =
165 { 0, 0, 0, 0 },
166 { 0, 0, 0x80, 0 },
167 { 0, 0x80, 0, 0 },
168 { 0, 0x80, 0x80, 0 },
169 { 0x80, 0, 0, 0 },
170 { 0x80, 0, 0x80, 0 },
171 { 0x80, 0x80, 0, 0 },
172 { 0x80, 0x80, 0x80, 0 },
173 { 0xC0, 0xC0, 0xC0, 0 },
174 { 0, 0, 0xFF, 0 },
175 { 0, 0xFF, 0, 0 },
176 { 0, 0xFF, 0xFF, 0 },
177 { 0xFF, 0, 0, 0 },
178 { 0xFF, 0, 0xFF, 0 },
179 { 0xFF, 0xFF, 0, 0 },
180 { 0xFF, 0xFF, 0xFF, 0 }
183 for (const auto& rPalEntry : aImplSalSysPalEntryAry)
185 if(rPalEntry.peRed == nRed &&
186 rPalEntry.peGreen == nGreen &&
187 rPalEntry.peBlue == nBlue)
189 return true;
193 return false;
196 bool IsExtraColor(BYTE nRed, BYTE nGreen, BYTE nBlue)
198 return (nRed == 0) && (nGreen == 184) && (nBlue == 255);
201 bool ImplIsPaletteEntry(BYTE nRed, BYTE nGreen, BYTE nBlue)
203 return IsDitherColor(nRed, nGreen, nBlue) ||
204 IsPaletteColor(nRed, nGreen, nBlue) ||
205 IsExtraColor(nRed, nGreen, nBlue);
208 } // namespace
210 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics& rParent):
211 mrParent(rParent),
212 mbXORMode(false),
213 mbPen(false),
214 mhPen(nullptr),
215 mbStockPen(false),
216 mbBrush(false),
217 mbStockBrush(false),
218 mhBrush(nullptr)
222 WinSalGraphicsImpl::~WinSalGraphicsImpl()
224 if ( mhPen )
226 if ( !mbStockPen )
227 DeletePen( mhPen );
230 if ( mhBrush )
232 if ( !mbStockBrush )
233 DeleteBrush( mhBrush );
237 void WinSalGraphicsImpl::Init()
241 void WinSalGraphicsImpl::freeResources()
245 bool WinSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uInt32)
247 return false;
250 void WinSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
252 HDC hSrcDC;
253 DWORD nRop;
255 if ( pSrcGraphics )
256 hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->getHDC();
257 else
258 hSrcDC = mrParent.getHDC();
260 if ( mbXORMode )
261 nRop = SRCINVERT;
262 else
263 nRop = SRCCOPY;
265 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
266 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
268 BitBlt( mrParent.getHDC(),
269 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
270 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
271 hSrcDC,
272 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
273 nRop );
275 else
277 int nOldStretchMode = SetStretchBltMode( mrParent.getHDC(), STRETCH_DELETESCANS );
278 StretchBlt( mrParent.getHDC(),
279 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
280 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
281 hSrcDC,
282 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
283 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
284 nRop );
285 SetStretchBltMode( mrParent.getHDC(), nOldStretchMode );
289 namespace
292 void MakeInvisibleArea(const RECT& rSrcRect,
293 int nLeft, int nTop, int nRight, int nBottom,
294 HRGN& rhInvalidateRgn)
296 if (!rhInvalidateRgn)
298 rhInvalidateRgn = CreateRectRgnIndirect(&rSrcRect);
301 ScopedHRGN hTempRgn(CreateRectRgn(nLeft, nTop, nRight, nBottom));
302 CombineRgn(rhInvalidateRgn, rhInvalidateRgn, hTempRgn.get(), RGN_DIFF);
305 void ImplCalcOutSideRgn( const RECT& rSrcRect,
306 int nLeft, int nTop, int nRight, int nBottom,
307 HRGN& rhInvalidateRgn )
309 // calculate area outside the visible region
310 if (rSrcRect.left < nLeft)
312 MakeInvisibleArea(rSrcRect, -31999, 0, nLeft, 31999, rhInvalidateRgn);
314 if (rSrcRect.top < nTop)
316 MakeInvisibleArea(rSrcRect, 0, -31999, 31999, nTop, rhInvalidateRgn);
318 if (rSrcRect.right > nRight)
320 MakeInvisibleArea(rSrcRect, nRight, 0, 31999, 31999, rhInvalidateRgn);
322 if (rSrcRect.bottom > nBottom)
324 MakeInvisibleArea(rSrcRect, 0, nBottom, 31999, 31999, rhInvalidateRgn);
328 } // namespace
330 void WinSalGraphicsImpl::copyArea( long nDestX, long nDestY,
331 long nSrcX, long nSrcY,
332 long nSrcWidth, long nSrcHeight,
333 bool bWindowInvalidate )
335 bool bRestoreClipRgn = false;
336 HRGN hOldClipRgn = nullptr;
337 int nOldClipRgnType = ERROR;
338 HRGN hInvalidateRgn = nullptr;
340 // do we have to invalidate also the overlapping regions?
341 if ( bWindowInvalidate && mrParent.isWindow() )
343 // compute and invalidate those parts that were either off-screen or covered by other windows
344 // while performing the above BitBlt
345 // those regions then have to be invalidated as they contain useless/wrong data
346 RECT aSrcRect;
347 RECT aClipRect;
348 RECT aTempRect;
349 RECT aTempRect2;
350 HRGN hTempRgn;
351 HWND hWnd;
353 // restrict srcRect to this window (calc intersection)
354 aSrcRect.left = static_cast<int>(nSrcX);
355 aSrcRect.top = static_cast<int>(nSrcY);
356 aSrcRect.right = aSrcRect.left+static_cast<int>(nSrcWidth);
357 aSrcRect.bottom = aSrcRect.top+static_cast<int>(nSrcHeight);
358 GetClientRect( mrParent.gethWnd(), &aClipRect );
359 if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
361 // transform srcRect to screen coordinates
362 POINT aPt;
363 aPt.x = 0;
364 aPt.y = 0;
365 ClientToScreen( mrParent.gethWnd(), &aPt );
366 aSrcRect.left += aPt.x;
367 aSrcRect.top += aPt.y;
368 aSrcRect.right += aPt.x;
369 aSrcRect.bottom += aPt.y;
370 hInvalidateRgn = nullptr;
372 // compute the parts that are off screen (ie invisible)
373 RECT theScreen;
374 ImplSalGetWorkArea( nullptr, &theScreen, nullptr ); // find the screen area taking multiple monitors into account
375 ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
377 // calculate regions that are covered by other windows
378 HRGN hTempRgn2 = nullptr;
379 HWND hWndTopWindow = mrParent.gethWnd();
380 // Find the TopLevel Window, because only Windows which are in
381 // in the foreground of our TopLevel window must be considered
382 if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
384 RECT aTempRect3 = aSrcRect;
387 hWndTopWindow = ::GetParent( hWndTopWindow );
389 // Test if the Parent clips our window
390 GetClientRect( hWndTopWindow, &aTempRect );
391 POINT aPt2;
392 aPt2.x = 0;
393 aPt2.y = 0;
394 ClientToScreen( hWndTopWindow, &aPt2 );
395 aTempRect.left += aPt2.x;
396 aTempRect.top += aPt2.y;
397 aTempRect.right += aPt2.x;
398 aTempRect.bottom += aPt2.y;
399 IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
401 while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
403 // If one or more Parents clip our window, then we must
404 // calculate the outside area
405 if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
407 ImplCalcOutSideRgn( aSrcRect,
408 aTempRect3.left, aTempRect3.top,
409 aTempRect3.right, aTempRect3.bottom,
410 hInvalidateRgn );
413 // retrieve the top-most (z-order) child window
414 hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
415 while ( hWnd )
417 if ( hWnd == hWndTopWindow )
418 break;
419 if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
421 GetWindowRect( hWnd, &aTempRect );
422 if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
424 // hWnd covers part or all of aSrcRect
425 if ( !hInvalidateRgn )
426 hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
428 // get full bounding box of hWnd
429 hTempRgn = CreateRectRgnIndirect( &aTempRect );
431 // get region of hWnd (the window may be shaped)
432 if ( !hTempRgn2 )
433 hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
434 int nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
435 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
437 // convert window region to screen coordinates
438 OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
439 // and intersect with the window's bounding box
440 CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
442 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
443 CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
444 DeleteRegion( hTempRgn );
447 // retrieve the next window in the z-order, i.e. the window below hwnd
448 hWnd = GetWindow( hWnd, GW_HWNDNEXT );
450 if ( hTempRgn2 )
451 DeleteRegion( hTempRgn2 );
452 if ( hInvalidateRgn )
454 // hInvalidateRgn contains the fully visible parts of the original srcRect
455 hTempRgn = CreateRectRgnIndirect( &aSrcRect );
456 // subtract it from the original rect to get the occluded parts
457 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
458 DeleteRegion( hTempRgn );
460 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
462 // move the occluded parts to the destination pos
463 int nOffX = static_cast<int>(nDestX-nSrcX);
464 int nOffY = static_cast<int>(nDestY-nSrcY);
465 OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
467 // by excluding hInvalidateRgn from the system's clip region
468 // we will prevent bitblt from copying useless data
469 // especially now shadows from overlapping windows will appear (#i36344)
470 hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
471 nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
473 bRestoreClipRgn = TRUE; // indicate changed clipregion and force invalidate
474 ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
480 BitBlt( mrParent.getHDC(),
481 static_cast<int>(nDestX), static_cast<int>(nDestY),
482 static_cast<int>(nSrcWidth), static_cast<int>(nSrcHeight),
483 mrParent.getHDC(),
484 static_cast<int>(nSrcX), static_cast<int>(nSrcY),
485 SRCCOPY );
487 if( bRestoreClipRgn )
489 // restore old clip region
490 if( nOldClipRgnType != ERROR )
491 SelectClipRgn( mrParent.getHDC(), hOldClipRgn);
492 DeleteRegion( hOldClipRgn );
494 // invalidate regions that were not copied
495 bool bInvalidate = true;
497 // Combine Invalidate vcl::Region with existing ClipRegion
498 HRGN hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
499 if ( GetClipRgn( mrParent.getHDC(), hTempRgn ) == 1 )
501 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
502 if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
503 bInvalidate = false;
505 DeleteRegion( hTempRgn );
507 if ( bInvalidate )
509 InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE );
510 // here we only initiate an update if this is the MainThread,
511 // so that there is no deadlock when handling the Paint event,
512 // as the SolarMutex is already held by this Thread
513 SalData* pSalData = GetSalData();
514 DWORD nCurThreadId = GetCurrentThreadId();
515 if ( pSalData->mnAppThreadId == nCurThreadId )
516 UpdateWindow( mrParent.gethWnd() );
519 DeleteRegion( hInvalidateRgn );
524 namespace {
526 void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSalBitmap,
527 bool bPrinter, int nDrawMode )
529 if( hDC )
531 HGLOBAL hDrawDIB;
532 HBITMAP hDrawDDB = rSalBitmap.ImplGethDDB();
533 std::unique_ptr<WinSalBitmap> xTmpSalBmp;
534 bool bPrintDDB = ( bPrinter && hDrawDDB );
536 if( bPrintDDB )
538 xTmpSalBmp.reset(new WinSalBitmap);
539 xTmpSalBmp->Create( rSalBitmap, rSalBitmap.GetBitCount() );
540 hDrawDIB = xTmpSalBmp->ImplGethDIB();
542 else
543 hDrawDIB = rSalBitmap.ImplGethDIB();
545 if( hDrawDIB )
547 PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
548 PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
549 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
550 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
552 StretchDIBits( hDC,
553 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
554 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
555 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY),
556 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
557 pBits, pBI, DIB_RGB_COLORS, nDrawMode );
559 GlobalUnlock( hDrawDIB );
560 SetStretchBltMode( hDC, nOldStretchMode );
562 else if( hDrawDDB && !bPrintDDB )
564 ScopedCachedHDC<CACHED_HDC_DRAW> hBmpDC(hDrawDDB);
566 COLORREF nOldBkColor = RGB(0xFF,0xFF,0xFF);
567 COLORREF nOldTextColor = RGB(0,0,0);
568 bool bMono = ( rSalBitmap.GetBitCount() == 1 );
570 if( bMono )
572 COLORREF nBkColor = RGB( 0xFF, 0xFF, 0xFF );
573 COLORREF nTextColor = RGB( 0x00, 0x00, 0x00 );
574 //fdo#33455 handle 1 bit depth pngs with palette entries
575 //to set fore/back colors
576 if (BitmapBuffer* pBitmapBuffer = const_cast<WinSalBitmap&>(rSalBitmap).AcquireBuffer(BitmapAccessMode::Info))
578 const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
579 if (rPalette.GetEntryCount() == 2)
581 Color nCol = rPalette[0];
582 nTextColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
583 nCol = rPalette[1];
584 nBkColor = RGB( nCol.GetRed(), nCol.GetGreen(), nCol.GetBlue() );
586 const_cast<WinSalBitmap&>(rSalBitmap).ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Info);
588 nOldBkColor = SetBkColor( hDC, nBkColor );
589 nOldTextColor = ::SetTextColor( hDC, nTextColor );
592 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
593 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
595 BitBlt( hDC,
596 static_cast<int>(rPosAry.mnDestX), static_cast<int>(rPosAry.mnDestY),
597 static_cast<int>(rPosAry.mnDestWidth), static_cast<int>(rPosAry.mnDestHeight),
598 hBmpDC.get(),
599 static_cast<int>(rPosAry.mnSrcX), static_cast<int>(rPosAry.mnSrcY),
600 nDrawMode );
602 else
604 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
606 StretchBlt( 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 static_cast<int>(rPosAry.mnSrcWidth), static_cast<int>(rPosAry.mnSrcHeight),
612 nDrawMode );
614 SetStretchBltMode( hDC, nOldStretchMode );
617 if( bMono )
619 SetBkColor( hDC, nOldBkColor );
620 ::SetTextColor( hDC, nOldTextColor );
626 } // namespace
628 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
630 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
632 if(bTryDirectPaint)
634 // only paint direct when no scaling and no MapMode, else the
635 // more expensive conversions may be done for short-time Bitmap/BitmapEx
636 // used for buffering only
637 if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
639 bTryDirectPaint = false;
643 // try to draw using GdiPlus directly
644 if(bTryDirectPaint && TryDrawBitmapGDIPlus(rPosAry, rSalBitmap))
646 return;
649 // fall back old stuff
650 assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
652 ImplDrawBitmap(mrParent.getHDC(), rPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
653 mrParent.isPrinter(),
654 mbXORMode ? SRCINVERT : SRCCOPY );
657 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
658 const SalBitmap& rSSalBitmap,
659 const SalBitmap& rSTransparentBitmap )
661 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
662 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
664 // try to draw using GdiPlus directly
665 if(bTryDirectPaint && drawAlphaBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap))
667 return;
670 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
671 assert(dynamic_cast<const WinSalBitmap*>(&rSTransparentBitmap));
673 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
674 const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
676 SalTwoRect aPosAry = rPosAry;
677 int nDstX = static_cast<int>(aPosAry.mnDestX);
678 int nDstY = static_cast<int>(aPosAry.mnDestY);
679 int nDstWidth = static_cast<int>(aPosAry.mnDestWidth);
680 int nDstHeight = static_cast<int>(aPosAry.mnDestHeight);
681 HDC hDC = mrParent.getHDC();
683 ScopedHBITMAP hMemBitmap;
684 ScopedHBITMAP hMaskBitmap;
686 if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
688 hMemBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
689 hMaskBitmap.reset(CreateCompatibleBitmap(hDC, nDstWidth, nDstHeight));
692 ScopedCachedHDC<CACHED_HDC_1> hMemDC(hMemBitmap.get());
693 ScopedCachedHDC<CACHED_HDC_2> hMaskDC(hMaskBitmap.get());
695 aPosAry.mnDestX = aPosAry.mnDestY = 0;
696 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
698 // WIN/WNT seems to have a minor problem mapping the correct color of the
699 // mask to the palette if we draw the DIB directly ==> draw DDB
700 if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
702 WinSalBitmap aTmp;
704 if( aTmp.Create( rTransparentBitmap, &mrParent ) )
705 ImplDrawBitmap( hMaskDC.get(), aPosAry, aTmp, false, SRCCOPY );
707 else
708 ImplDrawBitmap( hMaskDC.get(), aPosAry, rTransparentBitmap, false, SRCCOPY );
710 // now MemDC contains background, MaskDC the transparency mask
712 // #105055# Respect XOR mode
713 if( mbXORMode )
715 ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
716 // now MaskDC contains the bitmap area with black background
717 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCINVERT );
718 // now MemDC contains background XORed bitmap area ontop
720 else
722 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCAND );
723 // now MemDC contains background with masked-out bitmap area
724 ImplDrawBitmap( hMaskDC.get(), aPosAry, rSalBitmap, false, SRCERASE );
725 // now MaskDC contains the bitmap area with black background
726 BitBlt( hMemDC.get(), 0, 0, nDstWidth, nDstHeight, hMaskDC.get(), 0, 0, SRCPAINT );
727 // now MemDC contains background and bitmap merged together
729 // copy to output DC
730 BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC.get(), 0, 0, SRCCOPY );
733 bool WinSalGraphicsImpl::drawAlphaRect( long nX, long nY, long nWidth,
734 long nHeight, sal_uInt8 nTransparency )
736 if( mbPen || !mbBrush || mbXORMode )
737 return false; // can only perform solid fills without XOR.
739 ScopedCachedHDC<CACHED_HDC_1> hMemDC(nullptr);
740 SetPixel( hMemDC.get(), int(0), int(0), mnBrushColor );
742 BLENDFUNCTION aFunc = {
743 AC_SRC_OVER,
745 sal::static_int_cast<sal_uInt8>(255 - 255L*nTransparency/100),
749 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
750 // that to dest hdc
751 bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
752 hMemDC.get(), 0,0,1,1,
753 aFunc ) == TRUE;
755 return bRet;
758 void WinSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry,
759 const SalBitmap& rSSalBitmap,
760 Color nMaskColor)
762 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
764 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
766 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
768 SalTwoRect aPosAry = rPosAry;
769 const HDC hDC = mrParent.getHDC();
771 ScopedSelectedHBRUSH hBrush(hDC, CreateSolidBrush(RGB(nMaskColor.GetRed(),
772 nMaskColor.GetGreen(),
773 nMaskColor.GetBlue())));
775 // WIN/WNT seems to have a minor problem mapping the correct color of the
776 // mask to the palette if we draw the DIB directly ==> draw DDB
777 if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
779 WinSalBitmap aTmp;
781 if( aTmp.Create( rSalBitmap, &mrParent ) )
782 ImplDrawBitmap( hDC, aPosAry, aTmp, false, 0x00B8074AUL );
784 else
785 ImplDrawBitmap( hDC, aPosAry, rSalBitmap, false, 0x00B8074AUL );
788 std::shared_ptr<SalBitmap> WinSalGraphicsImpl::getBitmap( long nX, long nY, long nDX, long nDY )
790 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
792 std::shared_ptr<WinSalBitmap> pSalBitmap;
794 nDX = labs( nDX );
795 nDY = labs( nDY );
797 HDC hDC = mrParent.getHDC();
798 HBITMAP hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
799 bool bRet;
802 ScopedCachedHDC<CACHED_HDC_1> hBmpDC(hBmpBitmap);
804 bRet = BitBlt(hBmpDC.get(), 0, 0,
805 static_cast<int>(nDX), static_cast<int>(nDY), hDC,
806 static_cast<int>(nX), static_cast<int>(nY), SRCCOPY) ? TRUE : FALSE;
809 if( bRet )
811 pSalBitmap = std::make_shared<WinSalBitmap>();
813 if( !pSalBitmap->Create( hBmpBitmap, FALSE, FALSE ) )
815 pSalBitmap.reset();
818 else
820 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
821 DeleteBitmap( hBmpBitmap );
824 return pSalBitmap;
827 Color WinSalGraphicsImpl::getPixel( long nX, long nY )
829 COLORREF aWinCol = ::GetPixel( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY) );
831 if ( CLR_INVALID == aWinCol )
832 return Color( 0, 0, 0 );
833 else
834 return Color( GetRValue( aWinCol ),
835 GetGValue( aWinCol ),
836 GetBValue( aWinCol ) );
839 namespace
842 HBRUSH Get50PercentBrush()
844 SalData* pSalData = GetSalData();
845 if ( !pSalData->mh50Brush )
847 if ( !pSalData->mh50Bmp )
848 pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
849 pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
852 return pSalData->mh50Brush;
855 } // namespace
857 void WinSalGraphicsImpl::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
859 if ( nFlags & SalInvert::TrackFrame )
861 HPEN hDotPen = CreatePen( PS_DOT, 0, 0 );
862 HPEN hOldPen = SelectPen( mrParent.getHDC(), hDotPen );
863 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), GetStockBrush( NULL_BRUSH ) );
864 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
866 Rectangle( mrParent.getHDC(), static_cast<int>(nX), static_cast<int>(nY), static_cast<int>(nX+nWidth), static_cast<int>(nY+nHeight) );
868 SetROP2( mrParent.getHDC(), nOldROP );
869 SelectPen( mrParent.getHDC(), hOldPen );
870 SelectBrush( mrParent.getHDC(), hOldBrush );
871 DeletePen( hDotPen );
873 else if ( nFlags & SalInvert::N50 )
875 COLORREF nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
876 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), Get50PercentBrush() );
877 PatBlt( mrParent.getHDC(), nX, nY, nWidth, nHeight, PATINVERT );
878 ::SetTextColor( mrParent.getHDC(), nOldTextColor );
879 SelectBrush( mrParent.getHDC(), hOldBrush );
881 else
883 RECT aRect;
884 aRect.left = static_cast<int>(nX);
885 aRect.top = static_cast<int>(nY);
886 aRect.right = static_cast<int>(nX)+nWidth;
887 aRect.bottom = static_cast<int>(nY)+nHeight;
888 ::InvertRect( mrParent.getHDC(), &aRect );
892 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
894 HPEN hPen;
895 HPEN hOldPen;
896 HBRUSH hBrush;
897 HBRUSH hOldBrush = nullptr;
898 COLORREF nOldTextColor RGB(0,0,0);
899 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
901 if ( nSalFlags & SalInvert::TrackFrame )
902 hPen = CreatePen( PS_DOT, 0, 0 );
903 else
906 if ( nSalFlags & SalInvert::N50 )
907 hBrush = Get50PercentBrush();
908 else
909 hBrush = GetStockBrush( BLACK_BRUSH );
911 hPen = GetStockPen( NULL_PEN );
912 nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
913 hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
915 hOldPen = SelectPen( mrParent.getHDC(), hPen );
917 POINT const * pWinPtAry;
918 // for NT, we can handover the array directly
919 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
921 pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
922 // for Windows 95 and its maximum number of points
923 if ( nSalFlags & SalInvert::TrackFrame )
925 if ( !Polyline( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
926 Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
928 else
930 if ( !Polygon( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
931 Polygon( mrParent.getHDC(), pWinPtAry, 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 long WinSalGraphicsImpl::GetGraphicsWidth() const
953 if( mrParent.gethWnd() && IsWindow( mrParent.gethWnd() ) )
955 WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
956 if( pFrame )
958 if( pFrame->maGeometry.nWidth )
959 return pFrame->maGeometry.nWidth;
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 long nW(rectangle.GetWidth());
1170 const long nH(rectangle.GetHeight());
1172 if(nW && nH)
1174 const long nRight(rectangle.Left() + nW);
1175 const 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( long nX, 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( long nX, long nY )
1574 DrawPixelImpl( nX, nY, mnPenColor );
1577 void WinSalGraphicsImpl::drawPixel( long nX, 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( long nX1, long nY1, long nX2, 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( long nX, long nY, long nWidth, 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 SalPoint* pPtAry )
1627 // for NT, we can handover the array directly
1628 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1630 POINT const * pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
1632 // for Windows 95 and its maximum number of points
1633 if ( !Polyline( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
1634 Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
1636 // Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee)
1637 if ( !mrParent.isPrinter() )
1638 DrawPixelImpl( pWinPtAry[nPoints-1].x, pWinPtAry[nPoints-1].y, mnPenColor );
1641 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
1643 // for NT, we can handover the array directly
1644 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1646 POINT const * pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
1647 // for Windows 95 and its maximum number of points
1648 if ( !Polygon( mrParent.getHDC(), pWinPtAry, static_cast<int>(nPoints) ) && (nPoints > MAX_64KSALPOINTS) )
1649 Polygon( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
1652 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1653 PCONSTSALPOINT* pPtAry )
1655 UINT aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
1656 UINT* pWinPointAry;
1657 UINT nPolyPolyPoints = 0;
1658 UINT nPoints;
1659 UINT i;
1661 if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
1662 pWinPointAry = aWinPointAry;
1663 else
1664 pWinPointAry = new UINT[nPoly];
1666 for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1668 nPoints = static_cast<UINT>(pPoints[i])+1;
1669 pWinPointAry[i] = nPoints;
1670 nPolyPolyPoints += nPoints;
1673 POINT aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
1674 POINT* pWinPointAryAry;
1675 if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
1676 pWinPointAryAry = aWinPointAryAry;
1677 else
1678 pWinPointAryAry = new POINT[nPolyPolyPoints];
1679 // for NT, we can handover the array directly
1680 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1681 UINT n = 0;
1682 for ( i = 0; i < static_cast<UINT>(nPoly); i++ )
1684 nPoints = pWinPointAry[i];
1685 const SalPoint* pPolyAry = pPtAry[i];
1686 memcpy( pWinPointAryAry+n, pPolyAry, (nPoints-1)*sizeof(POINT) );
1687 pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
1688 n += nPoints;
1691 if ( !PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), static_cast<UINT>(nPoly) ) &&
1692 (nPolyPolyPoints > MAX_64KSALPOINTS) )
1694 nPolyPolyPoints = 0;
1695 nPoly = 0;
1698 nPolyPolyPoints += pWinPointAry[static_cast<UINT>(nPoly)];
1699 nPoly++;
1701 while ( nPolyPolyPoints < MAX_64KSALPOINTS );
1702 nPoly--;
1703 if ( pWinPointAry[static_cast<UINT>(nPoly)] > MAX_64KSALPOINTS )
1704 pWinPointAry[static_cast<UINT>(nPoly)] = MAX_64KSALPOINTS;
1705 if ( nPoly == 1 )
1706 Polygon( mrParent.getHDC(), pWinPointAryAry, *pWinPointAry );
1707 else
1708 PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), nPoly );
1711 if ( pWinPointAry != aWinPointAry )
1712 delete [] pWinPointAry;
1713 if ( pWinPointAryAry != aWinPointAryAry )
1714 delete [] pWinPointAryAry;
1717 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
1719 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1721 // #100127# draw an array of points which might also contain bezier control points
1722 if (!nPoints)
1723 return true;
1725 const HDC hdc = mrParent.getHDC();
1727 // TODO: profile whether the following options are faster:
1728 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
1729 // b) convert our flag array to window's and use PolyDraw
1730 MoveToEx(hdc, pPtAry->mnX, pPtAry->mnY, nullptr);
1731 ++pPtAry;
1732 ++pFlgAry;
1734 for(sal_uInt32 i = 1; i < nPoints; ++i)
1736 if(*pFlgAry != PolyFlags::Control)
1738 LineTo(hdc, pPtAry->mnX, pPtAry->mnY);
1740 else if(nPoints - i > 2)
1742 PolyBezierTo(hdc, reinterpret_cast<const POINT*>(pPtAry), 3);
1743 i += 2;
1744 pPtAry += 2;
1745 pFlgAry += 2;
1748 ++pPtAry;
1749 ++pFlgAry;
1752 return true;
1755 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
1757 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1759 POINT aStackAry1[SAL_POLY_STACKBUF];
1760 BYTE aStackAry2[SAL_POLY_STACKBUF];
1761 POINT* pWinPointAry;
1762 BYTE* pWinFlagAry;
1763 if( nPoints > SAL_POLY_STACKBUF )
1765 pWinPointAry = new POINT[ nPoints ];
1766 pWinFlagAry = new BYTE[ nPoints ];
1768 else
1770 pWinPointAry = aStackAry1;
1771 pWinFlagAry = aStackAry2;
1774 sal_uInt32 nPoints_i32(nPoints);
1775 ImplPreparePolyDraw(true, 1, &nPoints_i32, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
1777 bool bRet( false );
1779 if( BeginPath( mrParent.getHDC() ) )
1781 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nPoints);
1783 if( EndPath( mrParent.getHDC() ) )
1785 if( StrokeAndFillPath( mrParent.getHDC() ) )
1786 bRet = true;
1790 if( pWinPointAry != aStackAry1 )
1792 delete [] pWinPointAry;
1793 delete [] pWinFlagAry;
1796 return bRet;
1799 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1800 const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry )
1802 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1804 sal_uLong nCurrPoly, nTotalPoints;
1805 const sal_uInt32* pCurrPoints = pPoints;
1806 for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
1807 nTotalPoints += *pCurrPoints++;
1809 POINT aStackAry1[SAL_POLY_STACKBUF];
1810 BYTE aStackAry2[SAL_POLY_STACKBUF];
1811 POINT* pWinPointAry;
1812 BYTE* pWinFlagAry;
1813 if( nTotalPoints > SAL_POLY_STACKBUF )
1815 pWinPointAry = new POINT[ nTotalPoints ];
1816 pWinFlagAry = new BYTE[ nTotalPoints ];
1818 else
1820 pWinPointAry = aStackAry1;
1821 pWinFlagAry = aStackAry2;
1824 ImplPreparePolyDraw(true, nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
1826 bool bRet( false );
1828 if( BeginPath( mrParent.getHDC() ) )
1830 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nTotalPoints);
1832 if( EndPath( mrParent.getHDC() ) )
1834 if( StrokeAndFillPath( mrParent.getHDC() ) )
1835 bRet = true;
1839 if( pWinPointAry != aStackAry1 )
1841 delete [] pWinPointAry;
1842 delete [] pWinFlagAry;
1845 return bRet;
1848 static basegfx::B2DPoint impPixelSnap(
1849 const basegfx::B2DPolygon& rPolygon,
1850 const basegfx::B2DHomMatrix& rObjectToDevice,
1851 basegfx::B2DHomMatrix& rObjectToDeviceInv,
1852 sal_uInt32 nIndex)
1854 const sal_uInt32 nCount(rPolygon.count());
1856 // get the data
1857 const basegfx::B2ITuple aPrevTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + nCount - 1) % nCount)));
1858 const basegfx::B2DPoint aCurrPoint(rObjectToDevice * rPolygon.getB2DPoint(nIndex));
1859 const basegfx::B2ITuple aCurrTuple(basegfx::fround(aCurrPoint));
1860 const basegfx::B2ITuple aNextTuple(basegfx::fround(rObjectToDevice * rPolygon.getB2DPoint((nIndex + 1) % nCount)));
1862 // get the states
1863 const bool bPrevVertical(aPrevTuple.getX() == aCurrTuple.getX());
1864 const bool bNextVertical(aNextTuple.getX() == aCurrTuple.getX());
1865 const bool bPrevHorizontal(aPrevTuple.getY() == aCurrTuple.getY());
1866 const bool bNextHorizontal(aNextTuple.getY() == aCurrTuple.getY());
1867 const bool bSnapX(bPrevVertical || bNextVertical);
1868 const bool bSnapY(bPrevHorizontal || bNextHorizontal);
1870 if(bSnapX || bSnapY)
1872 basegfx::B2DPoint aSnappedPoint(
1873 bSnapX ? aCurrTuple.getX() : aCurrPoint.getX(),
1874 bSnapY ? aCurrTuple.getY() : aCurrPoint.getY());
1876 if(rObjectToDeviceInv.isIdentity())
1878 rObjectToDeviceInv = rObjectToDevice;
1879 rObjectToDeviceInv.invert();
1882 aSnappedPoint *= rObjectToDeviceInv;
1884 return aSnappedPoint;
1887 return rPolygon.getB2DPoint(nIndex);
1890 static void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1891 Gdiplus::GraphicsPath& rGraphicsPath,
1892 const basegfx::B2DPolygon& rPolygon,
1893 const basegfx::B2DHomMatrix& rObjectToDevice,
1894 bool bNoLineJoin,
1895 bool bPixelSnapHairline)
1897 sal_uInt32 nCount(rPolygon.count());
1899 if(nCount)
1901 const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
1903 if(nEdgeCount)
1905 const bool bControls(rPolygon.areControlPointsUsed());
1906 basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
1907 basegfx::B2DHomMatrix aObjectToDeviceInv;
1909 if(bPixelSnapHairline)
1911 aCurr = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, 0);
1914 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1916 const sal_uInt32 nNextIndex((a + 1) % nCount);
1917 basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
1918 const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
1919 const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
1921 if(bPixelSnapHairline)
1923 aNext = impPixelSnap(rPolygon, rObjectToDevice, aObjectToDeviceInv, nNextIndex);
1926 if(b1stControlPointUsed || b2ndControlPointUsed)
1928 basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
1929 basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
1931 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1932 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1933 // no 1st or 2nd control point, despite that these are mathematically correct definitions
1934 // (basegfx can handle that).
1935 // Caution: This error (and it's correction) might be necessary for other graphical
1936 // sub-systems in a similar way.
1937 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1938 // vector was wrong. Best alternative is one as close as possible which means short.
1939 if(!b1stControlPointUsed)
1941 aCa = aCurr + ((aCb - aCurr) * 0.0005);
1943 else if(!b2ndControlPointUsed)
1945 aCb = aNext + ((aCa - aNext) * 0.0005);
1948 rGraphicsPath.AddBezier(
1949 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1950 static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
1951 static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
1952 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1954 else
1956 rGraphicsPath.AddLine(
1957 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1958 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1961 if(a + 1 < nEdgeCount)
1963 aCurr = aNext;
1965 if(bNoLineJoin)
1967 rGraphicsPath.StartFigure();
1975 class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
1977 private:
1978 // the path data itself
1979 std::shared_ptr<Gdiplus::GraphicsPath> mpGraphicsPath;
1981 // all other values the triangulation is based on and
1982 // need to be compared with to check for data validity
1983 bool mbNoLineJoin;
1985 public:
1986 SystemDependentData_GraphicsPath(
1987 basegfx::SystemDependentDataManager& rSystemDependentDataManager,
1988 std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
1989 bool bNoLineJoin);
1991 // read access to Gdiplus::GraphicsPath
1992 std::shared_ptr<Gdiplus::GraphicsPath>& getGraphicsPath() { return mpGraphicsPath; }
1994 // other data-validity access
1995 bool getNoLineJoin() const { return mbNoLineJoin; }
1997 virtual sal_Int64 estimateUsageInBytes() const override;
2000 SystemDependentData_GraphicsPath::SystemDependentData_GraphicsPath(
2001 basegfx::SystemDependentDataManager& rSystemDependentDataManager,
2002 std::shared_ptr<Gdiplus::GraphicsPath>& rpGraphicsPath,
2003 bool bNoLineJoin)
2004 : basegfx::SystemDependentData(rSystemDependentDataManager),
2005 mpGraphicsPath(rpGraphicsPath),
2006 mbNoLineJoin(bNoLineJoin)
2010 sal_Int64 SystemDependentData_GraphicsPath::estimateUsageInBytes() const
2012 sal_Int64 nRetval(0);
2014 if(mpGraphicsPath)
2016 const INT nPointCount(mpGraphicsPath->GetPointCount());
2018 if(0 != nPointCount)
2020 // Each point has
2021 // - 2 x sizeof(Gdiplus::REAL)
2022 // - 1 byte (see GetPathTypes in docu)
2023 nRetval = nPointCount * ((2 * sizeof(Gdiplus::REAL)) + 1);
2027 return nRetval;
2030 bool WinSalGraphicsImpl::drawPolyPolygon(
2031 const basegfx::B2DHomMatrix& rObjectToDevice,
2032 const basegfx::B2DPolyPolygon& rPolyPolygon,
2033 double fTransparency)
2035 const sal_uInt32 nCount(rPolyPolygon.count());
2037 if(!mbBrush || 0 == nCount || fTransparency < 0.0 || fTransparency > 1.0)
2039 return true;
2042 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2043 const sal_uInt8 aTrans(sal_uInt8(255) - static_cast<sal_uInt8>(basegfx::fround(fTransparency * 255.0)));
2044 const Gdiplus::Color aTestColor(aTrans, maFillColor.GetRed(), maFillColor.GetGreen(), maFillColor.GetBlue());
2045 const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
2047 // Set full (Object-to-Device) transformation - if used
2048 if(rObjectToDevice.isIdentity())
2050 aGraphics.ResetTransform();
2052 else
2054 Gdiplus::Matrix aMatrix;
2056 aMatrix.SetElements(
2057 rObjectToDevice.get(0, 0),
2058 rObjectToDevice.get(1, 0),
2059 rObjectToDevice.get(0, 1),
2060 rObjectToDevice.get(1, 1),
2061 rObjectToDevice.get(0, 2),
2062 rObjectToDevice.get(1, 2));
2063 aGraphics.SetTransform(&aMatrix);
2066 // prepare local instance of Gdiplus::GraphicsPath
2067 std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
2069 // try to access buffered data
2070 std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
2071 rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
2073 if(pSystemDependentData_GraphicsPath)
2075 // copy buffered data
2076 pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
2078 else
2080 // Note: In principle we could use the same buffered geometry at line
2081 // and fill polygons. Checked that in a first try, used
2082 // GraphicsPath::AddPath from Gdiplus combined with below used
2083 // StartFigure/CloseFigure, worked well (thus the line-draw version
2084 // may create non-closed partial Polygon data).
2086 // But in current reality it gets not used due to e.g.
2087 // SdrPathPrimitive2D::create2DDecomposition creating transformed
2088 // line and fill polygon-primitives (what could be changed).
2090 // There will probably be more hindrances here in other rendering paths
2091 // which could all be found - intention to do this would be: Use more
2092 // transformations, less modifications of B2DPolygons/B2DPolyPolygons.
2094 // A fix for SdrPathPrimitive2D would be to create the sub-geometry
2095 // and embed into a TransformPrimitive2D containing the transformation.
2097 // A 2nd problem is that the NoLineJoin mode (basegfx::B2DLineJoin::NONE
2098 // && rLineWidths > 0.0) creates polygon fill infos that are not reusable
2099 // for the fill case (see ::drawPolyLine below) - thus we would need a
2100 // bool and/or two system-dependent paths buffered - doable, but complicated.
2102 // All in all: Make B2DPolyPolygon a SystemDependentDataProvider and buffer
2103 // the whole to-be-filled PolyPolygon independent from evtl. line-polygon
2104 // (at least for now...)
2106 // create data
2107 pGraphicsPath.reset(new Gdiplus::GraphicsPath());
2109 for(sal_uInt32 a(0); a < nCount; a++)
2111 if(0 != a)
2113 // #i101491# not needed for first run
2114 pGraphicsPath->StartFigure();
2117 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2118 *pGraphicsPath,
2119 rPolyPolygon.getB2DPolygon(a),
2120 rObjectToDevice, // not used due to the two 'false' values below, but to not forget later
2121 false,
2122 false);
2124 pGraphicsPath->CloseFigure();
2127 // add to buffering mechanism
2128 rPolyPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2129 ImplGetSystemDependentDataManager(),
2130 pGraphicsPath,
2131 false);
2134 if(mrParent.getAntiAliasB2DDraw())
2136 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2138 else
2140 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2143 if(mrParent.isPrinter())
2145 // #i121591#
2146 // Normally GdiPlus should not be used for printing at all since printers cannot
2147 // print transparent filled polygon geometry and normally this does not happen
2148 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2149 // and no transparent parts should remain for printing. But this can be overridden
2150 // by the user and thus happens. This call can only come (currently) from
2151 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2152 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2153 // checked that there is *no* transformation set and estimated that a stable factor
2154 // dependent of the printer's DPI is used. Create and set a transformation here to
2155 // correct this.
2156 const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2157 const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2159 // Now the transformation maybe/is already used (see above), so do
2160 // modify it without resetting to not destroy it.
2161 // I double-checked with MS docu that Gdiplus::MatrixOrderAppend does what
2162 // we need - in our notation, would be a multiply from left to execute
2163 // current transform first and this scale last.
2164 // I tried to trigger this code using Print from the menu and various
2165 // targets, but got no hit, thus maybe obsolete anyways. If someone knows
2166 // more, feel free to remove it.
2167 // One more hint: This *may* also be needed now in ::drawPolyLine below
2168 // since it also uses transformations now.
2170 // aGraphics.ResetTransform();
2172 aGraphics.ScaleTransform(
2173 Gdiplus::REAL(100.0) / aDpiX,
2174 Gdiplus::REAL(100.0) / aDpiY,
2175 Gdiplus::MatrixOrderAppend);
2178 // use created or buffered data
2179 aGraphics.FillPath(
2180 &aSolidBrush,
2181 &(*pGraphicsPath));
2183 return true;
2186 bool WinSalGraphicsImpl::drawPolyLine(
2187 const basegfx::B2DHomMatrix& rObjectToDevice,
2188 const basegfx::B2DPolygon& rPolygon,
2189 double fTransparency,
2190 const basegfx::B2DVector& rLineWidths,
2191 basegfx::B2DLineJoin eLineJoin,
2192 css::drawing::LineCap eLineCap,
2193 double fMiterMinimumAngle,
2194 bool bPixelSnapHairline)
2196 if(!mbPen || 0 == rPolygon.count())
2198 return true;
2201 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2202 const sal_uInt8 aTrans = static_cast<sal_uInt8>(basegfx::fround( 255 * (1.0 - fTransparency) ));
2203 const Gdiplus::Color aTestColor(aTrans, maLineColor.GetRed(), maLineColor.GetGreen(), maLineColor.GetBlue());
2204 Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(rLineWidths.getX()));
2205 bool bNoLineJoin(false);
2207 // Set full (Object-to-Device) transformation - if used
2208 if(rObjectToDevice.isIdentity())
2210 aGraphics.ResetTransform();
2212 else
2214 Gdiplus::Matrix aMatrix;
2216 aMatrix.SetElements(
2217 rObjectToDevice.get(0, 0),
2218 rObjectToDevice.get(1, 0),
2219 rObjectToDevice.get(0, 1),
2220 rObjectToDevice.get(1, 1),
2221 rObjectToDevice.get(0, 2),
2222 rObjectToDevice.get(1, 2));
2223 aGraphics.SetTransform(&aMatrix);
2226 switch(eLineJoin)
2228 case basegfx::B2DLineJoin::NONE :
2230 if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
2232 bNoLineJoin = true;
2234 break;
2236 case basegfx::B2DLineJoin::Bevel :
2238 aPen.SetLineJoin(Gdiplus::LineJoinBevel);
2239 break;
2241 case basegfx::B2DLineJoin::Miter :
2243 const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
2245 aPen.SetMiterLimit(aMiterLimit);
2246 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2247 // graphics, somewhere clipped in some distance from the edge point, dependent
2248 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2249 // that instead
2250 aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
2251 break;
2253 case basegfx::B2DLineJoin::Round :
2255 aPen.SetLineJoin(Gdiplus::LineJoinRound);
2256 break;
2260 switch(eLineCap)
2262 default: /*css::drawing::LineCap_BUTT*/
2264 // nothing to do
2265 break;
2267 case css::drawing::LineCap_ROUND:
2269 aPen.SetStartCap(Gdiplus::LineCapRound);
2270 aPen.SetEndCap(Gdiplus::LineCapRound);
2271 break;
2273 case css::drawing::LineCap_SQUARE:
2275 aPen.SetStartCap(Gdiplus::LineCapSquare);
2276 aPen.SetEndCap(Gdiplus::LineCapSquare);
2277 break;
2281 // prepare local instance of Gdiplus::GraphicsPath
2282 std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
2284 // try to access buffered data
2285 std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
2286 rPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>());
2288 if(pSystemDependentData_GraphicsPath)
2290 // check data validity
2291 if (pSystemDependentData_GraphicsPath->getNoLineJoin() != bNoLineJoin
2292 || bPixelSnapHairline /*tdf#124700*/)
2294 // data invalid, forget
2295 pSystemDependentData_GraphicsPath.reset();
2299 if(pSystemDependentData_GraphicsPath)
2301 // copy buffered data
2302 pGraphicsPath = pSystemDependentData_GraphicsPath->getGraphicsPath();
2304 else
2306 // fill data of buffered data
2307 pGraphicsPath.reset(new Gdiplus::GraphicsPath());
2309 impAddB2DPolygonToGDIPlusGraphicsPathReal(
2310 *pGraphicsPath,
2311 rPolygon,
2312 rObjectToDevice,
2313 bNoLineJoin,
2314 bPixelSnapHairline);
2316 if(rPolygon.isClosed() && !bNoLineJoin)
2318 // #i101491# needed to create the correct line joins
2319 pGraphicsPath->CloseFigure();
2322 // add to buffering mechanism
2323 if (!bPixelSnapHairline /*tdf#124700*/)
2325 rPolygon.addOrReplaceSystemDependentData<SystemDependentData_GraphicsPath>(
2326 ImplGetSystemDependentDataManager(),
2327 pGraphicsPath,
2328 bNoLineJoin);
2332 if(mrParent.getAntiAliasB2DDraw())
2334 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2336 else
2338 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2341 if(mrParent.isPrinter())
2343 // tdf#122384 As mentioned above in WinSalGraphicsImpl::drawPolyPolygon
2344 // (look for 'One more hint: This *may* also be needed now in'...).
2345 // See comments in same spot above *urgently* before doing changes here,
2346 // these comments are *still fully valid* at this place (!)
2347 const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2348 const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2350 aGraphics.ScaleTransform(
2351 Gdiplus::REAL(100.0) / aDpiX,
2352 Gdiplus::REAL(100.0) / aDpiY,
2353 Gdiplus::MatrixOrderAppend);
2356 aGraphics.DrawPath(
2357 &aPen,
2358 &(*pGraphicsPath));
2360 return true;
2363 static void paintToGdiPlus(
2364 Gdiplus::Graphics& rGraphics,
2365 const SalTwoRect& rTR,
2366 Gdiplus::Bitmap& rBitmap)
2368 // only parts of source are used
2369 Gdiplus::PointF aDestPoints[3];
2370 Gdiplus::ImageAttributes aAttributes;
2372 // define target region as paralellogram
2373 aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
2374 aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
2375 aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
2376 aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
2377 aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
2378 aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
2380 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2382 rGraphics.DrawImage(
2383 &rBitmap,
2384 aDestPoints,
2386 Gdiplus::REAL(rTR.mnSrcX),
2387 Gdiplus::REAL(rTR.mnSrcY),
2388 Gdiplus::REAL(rTR.mnSrcWidth),
2389 Gdiplus::REAL(rTR.mnSrcHeight),
2390 Gdiplus::UnitPixel,
2391 &aAttributes);
2394 static void setInterpolationMode(
2395 Gdiplus::Graphics& rGraphics,
2396 long rSrcWidth,
2397 long rDestWidth,
2398 long rSrcHeight,
2399 long rDestHeight)
2401 const bool bSameWidth(rSrcWidth == rDestWidth);
2402 const bool bSameHeight(rSrcHeight == rDestHeight);
2404 if(bSameWidth && bSameHeight)
2406 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
2408 else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
2410 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2412 else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
2414 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
2416 else
2418 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2422 bool WinSalGraphicsImpl::TryDrawBitmapGDIPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
2424 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2426 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2428 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2429 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
2431 if(aARGB.get())
2433 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2435 setInterpolationMode(
2436 aGraphics,
2437 rTR.mnSrcWidth,
2438 rTR.mnDestWidth,
2439 rTR.mnSrcHeight,
2440 rTR.mnDestHeight);
2442 paintToGdiPlus(
2443 aGraphics,
2444 rTR,
2445 *aARGB);
2447 return true;
2451 return false;
2454 bool WinSalGraphicsImpl::blendBitmap(
2455 const SalTwoRect&,
2456 const SalBitmap&)
2458 return false;
2461 bool WinSalGraphicsImpl::blendAlphaBitmap(
2462 const SalTwoRect&,
2463 const SalBitmap&,
2464 const SalBitmap&,
2465 const SalBitmap&)
2467 return false;
2470 bool WinSalGraphicsImpl::drawAlphaBitmap(
2471 const SalTwoRect& rTR,
2472 const SalBitmap& rSrcBitmap,
2473 const SalBitmap& rAlphaBmp)
2475 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2477 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2478 assert(dynamic_cast<const WinSalBitmap*>(&rAlphaBmp));
2480 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2481 const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
2482 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
2484 if(aARGB.get())
2486 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2488 setInterpolationMode(
2489 aGraphics,
2490 rTR.mnSrcWidth,
2491 rTR.mnDestWidth,
2492 rTR.mnSrcHeight,
2493 rTR.mnDestHeight);
2495 paintToGdiPlus(
2496 aGraphics,
2497 rTR,
2498 *aARGB);
2500 return true;
2504 return false;
2507 bool WinSalGraphicsImpl::drawTransformedBitmap(
2508 const basegfx::B2DPoint& rNull,
2509 const basegfx::B2DPoint& rX,
2510 const basegfx::B2DPoint& rY,
2511 const SalBitmap& rSourceBitmap,
2512 const SalBitmap* pAlphaBitmap)
2514 assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
2515 assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
2517 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
2518 const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
2519 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
2521 if(aARGB.get())
2523 const long nSrcWidth(aARGB->GetWidth());
2524 const long nSrcHeight(aARGB->GetHeight());
2526 if(nSrcWidth && nSrcHeight)
2528 const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
2529 const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
2531 if(nDestWidth && nDestHeight)
2533 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2534 Gdiplus::PointF aDestPoints[3];
2535 Gdiplus::ImageAttributes aAttributes;
2537 setInterpolationMode(
2538 aGraphics,
2539 nSrcWidth,
2540 nDestWidth,
2541 nSrcHeight,
2542 nDestHeight);
2544 // this mode is only capable of drawing the whole bitmap to a paralellogram
2545 aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
2546 aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
2547 aDestPoints[1].X = Gdiplus::REAL(rX.getX());
2548 aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
2549 aDestPoints[2].X = Gdiplus::REAL(rY.getX());
2550 aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
2552 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2554 aGraphics.DrawImage(
2555 aARGB.get(),
2556 aDestPoints,
2558 Gdiplus::REAL(0.0),
2559 Gdiplus::REAL(0.0),
2560 Gdiplus::REAL(nSrcWidth),
2561 Gdiplus::REAL(nSrcHeight),
2562 Gdiplus::UnitPixel,
2563 &aAttributes);
2567 return true;
2570 return false;
2573 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
2574 const Gradient& /*rGradient*/)
2576 return false;
2579 bool WinSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey& /*rControlCacheKey*/, int /*nX*/, int /*nY*/)
2581 return false;
2584 bool WinSalGraphicsImpl::RenderAndCacheNativeControl(OpenGLCompatibleDC& /*rWhite*/, OpenGLCompatibleDC& /*rBlack*/,
2585 int /*nX*/, int /*nY*/ , ControlCacheKey& /*aControlCacheKey*/)
2587 return false;
2590 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */