build fix
[LibreOffice.git] / vcl / win / gdi / gdiimpl.cxx
blob9a98ee7d6aaa93075745b33d91095ddb98df3e72
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 <tools/debug.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 <vcl/salbtype.hxx>
36 #include <win/salframe.h>
37 #include <basegfx/matrix/b2dhommatrixtools.hxx>
39 #include "outdata.hxx"
40 #include "win/salids.hrc"
42 #if defined _MSC_VER
43 #ifndef min
44 #define min(a,b) (((a) < (b)) ? (a) : (b))
45 #endif
46 #ifndef max
47 #define max(a,b) (((a) > (b)) ? (a) : (b))
48 #endif
49 #endif
51 #include "prewin.h"
53 #ifdef __MINGW32__
54 #ifdef GetObject
55 #undef GetObject
56 #endif
57 #endif
59 #include <gdiplus.h>
60 #include <gdiplusenums.h>
61 #include <gdipluscolor.h>
63 #include "postwin.h"
65 #define SAL_POLYPOLYCOUNT_STACKBUF 8
66 #define SAL_POLYPOLYPOINTS_STACKBUF 64
68 #define DITHER_PAL_DELTA 51
69 #define DITHER_MAX_SYSCOLOR 16
70 #define DMAP( _def_nVal, _def_nThres ) ((pDitherDiff[_def_nVal]>(_def_nThres))?pDitherHigh[_def_nVal]:pDitherLow[_def_nVal])
72 #define SAL_POLY_STACKBUF 32
74 namespace {
76 // #100127# draw an array of points which might also contain bezier control points
77 void ImplRenderPath( HDC hdc, sal_uLong nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
79 if( nPoints )
81 sal_uInt16 i;
82 // TODO: profile whether the following options are faster:
83 // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp.
84 // b) convert our flag array to window's and use PolyDraw
86 MoveToEx( hdc, pPtAry->mnX, pPtAry->mnY, nullptr );
87 ++pPtAry; ++pFlgAry;
89 for( i=1; i<nPoints; ++i, ++pPtAry, ++pFlgAry )
91 if( *pFlgAry != PolyFlags::Control )
93 LineTo( hdc, pPtAry->mnX, pPtAry->mnY );
95 else if( nPoints - i > 2 )
97 PolyBezierTo( hdc, reinterpret_cast<const POINT*>(pPtAry), 3 );
98 i += 2; pPtAry += 2; pFlgAry += 2;
104 // #100127# Fill point and flag memory from array of points which
105 // might also contain bezier control points for the PolyDraw() GDI method
106 // Make sure pWinPointAry and pWinFlagAry are big enough
107 void ImplPreparePolyDraw( bool bCloseFigures,
108 sal_uLong nPoly,
109 const sal_uInt32* pPoints,
110 const SalPoint* const* pPtAry,
111 const PolyFlags* const* pFlgAry,
112 POINT* pWinPointAry,
113 BYTE* pWinFlagAry )
115 sal_uLong nCurrPoly;
116 for( nCurrPoly=0; nCurrPoly<nPoly; ++nCurrPoly )
118 const POINT* pCurrPoint = reinterpret_cast<const POINT*>( *pPtAry++ );
119 const PolyFlags* pCurrFlag = *pFlgAry++;
120 const sal_uInt32 nCurrPoints = *pPoints++;
121 const bool bHaveFlagArray( pCurrFlag );
122 sal_uLong nCurrPoint;
124 if( nCurrPoints )
126 // start figure
127 *pWinPointAry++ = *pCurrPoint++;
128 *pWinFlagAry++ = PT_MOVETO;
129 ++pCurrFlag;
131 for( nCurrPoint=1; nCurrPoint<nCurrPoints; )
133 // #102067# Check existence of flag array
134 if( bHaveFlagArray &&
135 ( nCurrPoint + 2 ) < nCurrPoints )
137 PolyFlags P4( pCurrFlag[ 2 ] );
139 if( ( PolyFlags::Control == pCurrFlag[ 0 ] ) &&
140 ( PolyFlags::Control == pCurrFlag[ 1 ] ) &&
141 ( PolyFlags::Normal == P4 || PolyFlags::Smooth == P4 || PolyFlags::Symmetric == P4 ) )
143 // control point one
144 *pWinPointAry++ = *pCurrPoint++;
145 *pWinFlagAry++ = PT_BEZIERTO;
147 // control point two
148 *pWinPointAry++ = *pCurrPoint++;
149 *pWinFlagAry++ = PT_BEZIERTO;
151 // end point
152 *pWinPointAry++ = *pCurrPoint++;
153 *pWinFlagAry++ = PT_BEZIERTO;
155 nCurrPoint += 3;
156 pCurrFlag += 3;
157 continue;
161 // regular line point
162 *pWinPointAry++ = *pCurrPoint++;
163 *pWinFlagAry++ = PT_LINETO;
164 ++pCurrFlag;
165 ++nCurrPoint;
168 // end figure?
169 if( bCloseFigures )
170 pWinFlagAry[-1] |= PT_CLOSEFIGURE;
176 static PALETTEENTRY aImplSalSysPalEntryAry[ DITHER_MAX_SYSCOLOR ] =
178 { 0, 0, 0, 0 },
179 { 0, 0, 0x80, 0 },
180 { 0, 0x80, 0, 0 },
181 { 0, 0x80, 0x80, 0 },
182 { 0x80, 0, 0, 0 },
183 { 0x80, 0, 0x80, 0 },
184 { 0x80, 0x80, 0, 0 },
185 { 0x80, 0x80, 0x80, 0 },
186 { 0xC0, 0xC0, 0xC0, 0 },
187 { 0, 0, 0xFF, 0 },
188 { 0, 0xFF, 0, 0 },
189 { 0, 0xFF, 0xFF, 0 },
190 { 0xFF, 0, 0, 0 },
191 { 0xFF, 0, 0xFF, 0 },
192 { 0xFF, 0xFF, 0, 0 },
193 { 0xFF, 0xFF, 0xFF, 0 }
196 static PALETTEENTRY aImplExtraColor1 =
198 0, 184, 255, 0
201 static BYTE aOrdDither8Bit[8][8] =
203 { 0, 38, 9, 48, 2, 40, 12, 50 },
204 { 25, 12, 35, 22, 28, 15, 37, 24 },
205 { 6, 44, 3, 41, 8, 47, 5, 44 },
206 { 32, 19, 28, 16, 34, 21, 31, 18 },
207 { 1, 40, 11, 49, 0, 39, 10, 48 },
208 { 27, 14, 36, 24, 26, 13, 36, 23 },
209 { 8, 46, 4, 43, 7, 45, 4, 42 },
210 { 33, 20, 30, 17, 32, 20, 29, 16 }
213 static BYTE aOrdDither16Bit[8][8] =
215 { 0, 6, 1, 7, 0, 6, 1, 7 },
216 { 4, 2, 5, 3, 4, 2, 5, 3 },
217 { 1, 7, 0, 6, 1, 7, 0, 6 },
218 { 5, 3, 4, 2, 5, 3, 4, 2 },
219 { 0, 6, 1, 7, 0, 6, 1, 7 },
220 { 4, 2, 5, 3, 4, 2, 5, 3 },
221 { 1, 7, 0, 6, 1, 7, 0, 6 },
222 { 5, 3, 4, 2, 5, 3, 4, 2 }
225 SalColor ImplGetROPSalColor( SalROPColor nROPColor )
227 SalColor nSalColor;
228 if ( nROPColor == SalROPColor::N0 )
229 nSalColor = MAKE_SALCOLOR( 0, 0, 0 );
230 else
231 nSalColor = MAKE_SALCOLOR( 255, 255, 255 );
232 return nSalColor;
235 int ImplIsPaletteEntry( BYTE nRed, BYTE nGreen, BYTE nBlue )
237 // dither color?
238 if ( !(nRed % DITHER_PAL_DELTA) && !(nGreen % DITHER_PAL_DELTA) && !(nBlue % DITHER_PAL_DELTA) )
239 return TRUE;
241 PALETTEENTRY* pPalEntry = aImplSalSysPalEntryAry;
243 // standard palette color?
244 for ( sal_uInt16 i = 0; i < DITHER_MAX_SYSCOLOR; i++, pPalEntry++ )
246 if( pPalEntry->peRed == nRed && pPalEntry->peGreen == nGreen && pPalEntry->peBlue == nBlue )
247 return TRUE;
250 // extra color?
251 if ( aImplExtraColor1.peRed == nRed &&
252 aImplExtraColor1.peGreen == nGreen &&
253 aImplExtraColor1.peBlue == nBlue )
255 return TRUE;
258 return FALSE;
263 WinSalGraphicsImpl::WinSalGraphicsImpl(WinSalGraphics& rParent):
264 mrParent(rParent),
265 mbXORMode(false),
266 mbPen(false),
267 mhPen(nullptr),
268 mbStockPen(false),
269 mbBrush(false),
270 mbStockBrush(false),
271 mhBrush(nullptr)
275 WinSalGraphicsImpl::~WinSalGraphicsImpl()
277 if ( mhPen )
279 if ( !mbStockPen )
280 DeletePen( mhPen );
283 if ( mhBrush )
285 if ( !mbStockBrush )
286 DeleteBrush( mhBrush );
291 void WinSalGraphicsImpl::Init()
295 void WinSalGraphicsImpl::freeResources()
299 bool WinSalGraphicsImpl::drawEPS(long, long, long, long, void*, sal_uLong)
301 return false;
304 void WinSalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics* pSrcGraphics )
306 HDC hSrcDC;
307 DWORD nRop;
309 if ( pSrcGraphics )
310 hSrcDC = static_cast<WinSalGraphics*>(pSrcGraphics)->getHDC();
311 else
312 hSrcDC = mrParent.getHDC();
314 if ( mbXORMode )
315 nRop = SRCINVERT;
316 else
317 nRop = SRCCOPY;
319 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
320 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
322 BitBlt( mrParent.getHDC(),
323 (int)rPosAry.mnDestX, (int)rPosAry.mnDestY,
324 (int)rPosAry.mnDestWidth, (int)rPosAry.mnDestHeight,
325 hSrcDC,
326 (int)rPosAry.mnSrcX, (int)rPosAry.mnSrcY,
327 nRop );
329 else
331 int nOldStretchMode = SetStretchBltMode( mrParent.getHDC(), STRETCH_DELETESCANS );
332 StretchBlt( mrParent.getHDC(),
333 (int)rPosAry.mnDestX, (int)rPosAry.mnDestY,
334 (int)rPosAry.mnDestWidth, (int)rPosAry.mnDestHeight,
335 hSrcDC,
336 (int)rPosAry.mnSrcX, (int)rPosAry.mnSrcY,
337 (int)rPosAry.mnSrcWidth, (int)rPosAry.mnSrcHeight,
338 nRop );
339 SetStretchBltMode( mrParent.getHDC(), nOldStretchMode );
343 void ImplCalcOutSideRgn( const RECT& rSrcRect,
344 int nLeft, int nTop, int nRight, int nBottom,
345 HRGN& rhInvalidateRgn )
347 HRGN hTempRgn;
349 // calculate area outside the visible region
350 if ( rSrcRect.left < nLeft )
352 if ( !rhInvalidateRgn )
353 rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
354 hTempRgn = CreateRectRgn( -31999, 0, nLeft, 31999 );
355 CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
356 DeleteRegion( hTempRgn );
358 if ( rSrcRect.top < nTop )
360 if ( !rhInvalidateRgn )
361 rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
362 hTempRgn = CreateRectRgn( 0, -31999, 31999, nTop );
363 CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
364 DeleteRegion( hTempRgn );
366 if ( rSrcRect.right > nRight )
368 if ( !rhInvalidateRgn )
369 rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
370 hTempRgn = CreateRectRgn( nRight, 0, 31999, 31999 );
371 CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
372 DeleteRegion( hTempRgn );
374 if ( rSrcRect.bottom > nBottom )
376 if ( !rhInvalidateRgn )
377 rhInvalidateRgn = CreateRectRgnIndirect( &rSrcRect );
378 hTempRgn = CreateRectRgn( 0, nBottom, 31999, 31999 );
379 CombineRgn( rhInvalidateRgn, rhInvalidateRgn, hTempRgn, RGN_DIFF );
380 DeleteRegion( hTempRgn );
384 void WinSalGraphicsImpl::copyArea( long nDestX, long nDestY,
385 long nSrcX, long nSrcY,
386 long nSrcWidth, long nSrcHeight,
387 bool bWindowInvalidate )
389 bool bRestoreClipRgn = false;
390 HRGN hOldClipRgn = nullptr;
391 int nOldClipRgnType = ERROR;
392 HRGN hInvalidateRgn = nullptr;
394 // do we have to invalidate also the overlapping regions?
395 if ( bWindowInvalidate && mrParent.isWindow() )
397 // compute and invalidate those parts that were either off-screen or covered by other windows
398 // while performing the above BitBlt
399 // those regions then have to be invalidated as they contain useless/wrong data
400 RECT aSrcRect;
401 RECT aClipRect;
402 RECT aTempRect;
403 RECT aTempRect2;
404 HRGN hTempRgn;
405 HWND hWnd;
407 // restrict srcRect to this window (calc intersection)
408 aSrcRect.left = (int)nSrcX;
409 aSrcRect.top = (int)nSrcY;
410 aSrcRect.right = aSrcRect.left+(int)nSrcWidth;
411 aSrcRect.bottom = aSrcRect.top+(int)nSrcHeight;
412 GetClientRect( mrParent.gethWnd(), &aClipRect );
413 if ( IntersectRect( &aSrcRect, &aSrcRect, &aClipRect ) )
415 // transform srcRect to screen coordinates
416 POINT aPt;
417 aPt.x = 0;
418 aPt.y = 0;
419 ClientToScreen( mrParent.gethWnd(), &aPt );
420 aSrcRect.left += aPt.x;
421 aSrcRect.top += aPt.y;
422 aSrcRect.right += aPt.x;
423 aSrcRect.bottom += aPt.y;
424 hInvalidateRgn = nullptr;
426 // compute the parts that are off screen (ie invisible)
427 RECT theScreen;
428 ImplSalGetWorkArea( nullptr, &theScreen, nullptr ); // find the screen area taking multiple monitors into account
429 ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
431 // calculate regions that are covered by other windows
432 HRGN hTempRgn2 = nullptr;
433 HWND hWndTopWindow = mrParent.gethWnd();
434 // Find the TopLevel Window, because only Windows which are in
435 // in the foreground of our TopLevel window must be considered
436 if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
438 RECT aTempRect3 = aSrcRect;
441 hWndTopWindow = ::GetParent( hWndTopWindow );
443 // Test if the Parent clips our window
444 GetClientRect( hWndTopWindow, &aTempRect );
445 POINT aPt2;
446 aPt2.x = 0;
447 aPt2.y = 0;
448 ClientToScreen( hWndTopWindow, &aPt2 );
449 aTempRect.left += aPt2.x;
450 aTempRect.top += aPt2.y;
451 aTempRect.right += aPt2.x;
452 aTempRect.bottom += aPt2.y;
453 IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
455 while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
457 // If one or more Parents clip our window, than we must
458 // calculate the outside area
459 if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
461 ImplCalcOutSideRgn( aSrcRect,
462 aTempRect3.left, aTempRect3.top,
463 aTempRect3.right, aTempRect3.bottom,
464 hInvalidateRgn );
467 // retrieve the top-most (z-order) child window
468 hWnd = GetWindow( GetDesktopWindow(), GW_CHILD );
469 while ( hWnd )
471 if ( hWnd == hWndTopWindow )
472 break;
473 if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
475 GetWindowRect( hWnd, &aTempRect );
476 if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
478 // hWnd covers part or all of aSrcRect
479 if ( !hInvalidateRgn )
480 hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
482 // get full bounding box of hWnd
483 hTempRgn = CreateRectRgnIndirect( &aTempRect );
485 // get region of hWnd (the window may be shaped)
486 if ( !hTempRgn2 )
487 hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 );
488 int nRgnType = GetWindowRgn( hWnd, hTempRgn2 );
489 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
491 // convert window region to screen coordinates
492 OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top );
493 // and intersect with the window's bounding box
494 CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
496 // finally compute that part of aSrcRect which is not covered by any parts of hWnd
497 CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
498 DeleteRegion( hTempRgn );
501 // retrieve the next window in the z-order, i.e. the window below hwnd
502 hWnd = GetWindow( hWnd, GW_HWNDNEXT );
504 if ( hTempRgn2 )
505 DeleteRegion( hTempRgn2 );
506 if ( hInvalidateRgn )
508 // hInvalidateRgn contains the fully visible parts of the original srcRect
509 hTempRgn = CreateRectRgnIndirect( &aSrcRect );
510 // subtract it from the original rect to get the occluded parts
511 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
512 DeleteRegion( hTempRgn );
514 if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
516 // move the occluded parts to the destination pos
517 int nOffX = (int)(nDestX-nSrcX);
518 int nOffY = (int)(nDestY-nSrcY);
519 OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
521 // by excluding hInvalidateRgn from the system's clip region
522 // we will prevent bitblt from copying useless data
523 // especially now shadows from overlapping windows will appear (#i36344)
524 hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
525 nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
527 bRestoreClipRgn = TRUE; // indicate changed clipregion and force invalidate
528 ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
534 BitBlt( mrParent.getHDC(),
535 (int)nDestX, (int)nDestY,
536 (int)nSrcWidth, (int)nSrcHeight,
537 mrParent.getHDC(),
538 (int)nSrcX, (int)nSrcY,
539 SRCCOPY );
541 if( bRestoreClipRgn )
543 // restore old clip region
544 if( nOldClipRgnType != ERROR )
545 SelectClipRgn( mrParent.getHDC(), hOldClipRgn);
546 DeleteRegion( hOldClipRgn );
548 // invalidate regions that were not copied
549 bool bInvalidate = true;
551 // Combine Invalidate vcl::Region with existing ClipRegion
552 HRGN hTempRgn = CreateRectRgn( 0, 0, 0, 0 );
553 if ( GetClipRgn( mrParent.getHDC(), hTempRgn ) == 1 )
555 int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_AND );
556 if ( (nRgnType == ERROR) || (nRgnType == NULLREGION) )
557 bInvalidate = false;
559 DeleteRegion( hTempRgn );
561 if ( bInvalidate )
563 InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE );
564 // here we only initiate an update if this is the MainThread,
565 // so that there is no deadlock when handling the Paint event,
566 // as the SolarMutex is already held by this Thread
567 SalData* pSalData = GetSalData();
568 DWORD nCurThreadId = GetCurrentThreadId();
569 if ( pSalData->mnAppThreadId == nCurThreadId )
570 UpdateWindow( mrParent.gethWnd() );
573 DeleteRegion( hInvalidateRgn );
578 namespace {
580 void ImplDrawBitmap( HDC hDC, const SalTwoRect& rPosAry, const WinSalBitmap& rSalBitmap,
581 bool bPrinter, int nDrawMode )
583 if( hDC )
585 HGLOBAL hDrawDIB;
586 HBITMAP hDrawDDB = rSalBitmap.ImplGethDDB();
587 WinSalBitmap* pTmpSalBmp = nullptr;
588 bool bPrintDDB = ( bPrinter && hDrawDDB );
590 if( bPrintDDB )
592 pTmpSalBmp = new WinSalBitmap;
593 pTmpSalBmp->Create( rSalBitmap, rSalBitmap.GetBitCount() );
594 hDrawDIB = pTmpSalBmp->ImplGethDIB();
596 else
597 hDrawDIB = rSalBitmap.ImplGethDIB();
599 if( hDrawDIB )
601 PBITMAPINFO pBI = static_cast<PBITMAPINFO>(GlobalLock( hDrawDIB ));
602 PBYTE pBits = reinterpret_cast<PBYTE>(pBI) + pBI->bmiHeader.biSize +
603 WinSalBitmap::ImplGetDIBColorCount( hDrawDIB ) * sizeof( RGBQUAD );
604 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
606 StretchDIBits( hDC,
607 (int)rPosAry.mnDestX, (int)rPosAry.mnDestY,
608 (int)rPosAry.mnDestWidth, (int)rPosAry.mnDestHeight,
609 (int)rPosAry.mnSrcX, (int)(pBI->bmiHeader.biHeight - rPosAry.mnSrcHeight - rPosAry.mnSrcY),
610 (int)rPosAry.mnSrcWidth, (int)rPosAry.mnSrcHeight,
611 pBits, pBI, DIB_RGB_COLORS, nDrawMode );
613 GlobalUnlock( hDrawDIB );
614 SetStretchBltMode( hDC, nOldStretchMode );
616 else if( hDrawDDB && !bPrintDDB )
618 HDC hBmpDC = ImplGetCachedDC( CACHED_HDC_DRAW, hDrawDDB );
619 COLORREF nOldBkColor = RGB(0xFF,0xFF,0xFF);
620 COLORREF nOldTextColor = RGB(0,0,0);
621 bool bMono = ( rSalBitmap.GetBitCount() == 1 );
623 if( bMono )
625 COLORREF nBkColor = RGB( 0xFF, 0xFF, 0xFF );
626 COLORREF nTextColor = RGB( 0x00, 0x00, 0x00 );
627 //fdo#33455 handle 1 bit depth pngs with palette entries
628 //to set fore/back colors
629 if (BitmapBuffer* pBitmapBuffer = const_cast<WinSalBitmap&>(rSalBitmap).AcquireBuffer(BitmapAccessMode::Info))
631 const BitmapPalette& rPalette = pBitmapBuffer->maPalette;
632 if (rPalette.GetEntryCount() == 2)
634 SalColor nCol;
635 nCol = ImplColorToSal(rPalette[0]);
636 nTextColor = RGB( SALCOLOR_RED(nCol), SALCOLOR_GREEN(nCol), SALCOLOR_BLUE(nCol) );
637 nCol = ImplColorToSal(rPalette[1]);
638 nBkColor = RGB( SALCOLOR_RED(nCol), SALCOLOR_GREEN(nCol), SALCOLOR_BLUE(nCol) );
640 const_cast<WinSalBitmap&>(rSalBitmap).ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Info);
642 nOldBkColor = SetBkColor( hDC, nBkColor );
643 nOldTextColor = ::SetTextColor( hDC, nTextColor );
646 if ( (rPosAry.mnSrcWidth == rPosAry.mnDestWidth) &&
647 (rPosAry.mnSrcHeight == rPosAry.mnDestHeight) )
649 BitBlt( hDC,
650 (int)rPosAry.mnDestX, (int)rPosAry.mnDestY,
651 (int)rPosAry.mnDestWidth, (int)rPosAry.mnDestHeight,
652 hBmpDC,
653 (int)rPosAry.mnSrcX, (int)rPosAry.mnSrcY,
654 nDrawMode );
656 else
658 const int nOldStretchMode = SetStretchBltMode( hDC, STRETCH_DELETESCANS );
660 StretchBlt( hDC,
661 (int)rPosAry.mnDestX, (int)rPosAry.mnDestY,
662 (int)rPosAry.mnDestWidth, (int)rPosAry.mnDestHeight,
663 hBmpDC,
664 (int)rPosAry.mnSrcX, (int)rPosAry.mnSrcY,
665 (int)rPosAry.mnSrcWidth, (int)rPosAry.mnSrcHeight,
666 nDrawMode );
668 SetStretchBltMode( hDC, nOldStretchMode );
671 if( bMono )
673 SetBkColor( hDC, nOldBkColor );
674 ::SetTextColor( hDC, nOldTextColor );
677 ImplReleaseCachedDC( CACHED_HDC_DRAW );
680 if( bPrintDDB )
681 delete pTmpSalBmp;
687 void WinSalGraphicsImpl::drawBitmap(const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap)
689 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
691 if(bTryDirectPaint)
693 // only paint direct when no scaling and no MapMode, else the
694 // more expensive conversions may be done for short-time Bitmap/BitmapEx
695 // used for buffering only
696 if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
698 bTryDirectPaint = false;
702 // try to draw using GdiPlus directly
703 if(bTryDirectPaint && tryDrawBitmapGdiPlus(rPosAry, rSalBitmap))
705 return;
708 // fall back old stuff
709 assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
711 ImplDrawBitmap(mrParent.getHDC(), rPosAry, static_cast<const WinSalBitmap&>(rSalBitmap),
712 mrParent.isPrinter(),
713 mbXORMode ? SRCINVERT : SRCCOPY );
716 void WinSalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry,
717 const SalBitmap& rSSalBitmap,
718 const SalBitmap& rSTransparentBitmap )
720 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
721 bool bTryDirectPaint(!mrParent.isPrinter() && !mbXORMode);
723 // try to draw using GdiPlus directly
724 if(bTryDirectPaint && drawAlphaBitmap(rPosAry, rSSalBitmap, rSTransparentBitmap))
726 return;
729 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
730 assert(dynamic_cast<const WinSalBitmap*>(&rSTransparentBitmap));
732 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
733 const WinSalBitmap& rTransparentBitmap = static_cast<const WinSalBitmap&>(rSTransparentBitmap);
735 SalTwoRect aPosAry = rPosAry;
736 int nDstX = (int)aPosAry.mnDestX;
737 int nDstY = (int)aPosAry.mnDestY;
738 int nDstWidth = (int)aPosAry.mnDestWidth;
739 int nDstHeight = (int)aPosAry.mnDestHeight;
740 HDC hDC = mrParent.getHDC();
741 HBITMAP hMemBitmap = nullptr;
742 HBITMAP hMaskBitmap = nullptr;
744 if( ( nDstWidth > CACHED_HDC_DEFEXT ) || ( nDstHeight > CACHED_HDC_DEFEXT ) )
746 hMemBitmap = CreateCompatibleBitmap( hDC, nDstWidth, nDstHeight );
747 hMaskBitmap = CreateCompatibleBitmap( hDC, nDstWidth, nDstHeight );
750 HDC hMemDC = ImplGetCachedDC( CACHED_HDC_1, hMemBitmap );
751 HDC hMaskDC = ImplGetCachedDC( CACHED_HDC_2, hMaskBitmap );
753 aPosAry.mnDestX = aPosAry.mnDestY = 0;
754 BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hDC, nDstX, nDstY, SRCCOPY );
756 // WIN/WNT seems to have a minor problem mapping the correct color of the
757 // mask to the palette if we draw the DIB directly ==> draw DDB
758 if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
760 WinSalBitmap aTmp;
762 if( aTmp.Create( rTransparentBitmap, &mrParent ) )
763 ImplDrawBitmap( hMaskDC, aPosAry, aTmp, false, SRCCOPY );
765 else
766 ImplDrawBitmap( hMaskDC, aPosAry, rTransparentBitmap, false, SRCCOPY );
768 // now MemDC contains background, MaskDC the transparency mask
770 // #105055# Respect XOR mode
771 if( mbXORMode )
773 ImplDrawBitmap( hMaskDC, aPosAry, rSalBitmap, false, SRCERASE );
774 // now MaskDC contains the bitmap area with black background
775 BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCINVERT );
776 // now MemDC contains background XORed bitmap area ontop
778 else
780 BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCAND );
781 // now MemDC contains background with masked-out bitmap area
782 ImplDrawBitmap( hMaskDC, aPosAry, rSalBitmap, false, SRCERASE );
783 // now MaskDC contains the bitmap area with black background
784 BitBlt( hMemDC, 0, 0, nDstWidth, nDstHeight, hMaskDC, 0, 0, SRCPAINT );
785 // now MemDC contains background and bitmap merged together
787 // copy to output DC
788 BitBlt( hDC, nDstX, nDstY, nDstWidth, nDstHeight, hMemDC, 0, 0, SRCCOPY );
790 ImplReleaseCachedDC( CACHED_HDC_1 );
791 ImplReleaseCachedDC( CACHED_HDC_2 );
793 // hMemBitmap != 0 ==> hMaskBitmap != 0
794 if( hMemBitmap )
796 DeleteObject( hMemBitmap );
797 DeleteObject( hMaskBitmap );
801 bool WinSalGraphicsImpl::drawAlphaRect( long nX, long nY, long nWidth,
802 long nHeight, sal_uInt8 nTransparency )
804 if( mbPen || !mbBrush || mbXORMode )
805 return false; // can only perform solid fills without XOR.
807 HDC hMemDC = ImplGetCachedDC( CACHED_HDC_1 );
808 SetPixel( hMemDC, (int)0, (int)0, mnBrushColor );
810 BLENDFUNCTION aFunc = {
811 AC_SRC_OVER,
813 sal::static_int_cast<sal_uInt8>(255 - 255L*nTransparency/100),
817 // hMemDC contains a 1x1 bitmap of the right color - stretch-blit
818 // that to dest hdc
819 bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
820 hMemDC, 0,0,1,1,
821 aFunc ) == TRUE;
823 ImplReleaseCachedDC( CACHED_HDC_1 );
825 return bRet;
828 void WinSalGraphicsImpl::drawMask( const SalTwoRect& rPosAry,
829 const SalBitmap& rSSalBitmap,
830 SalColor nMaskColor )
832 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
834 assert(dynamic_cast<const WinSalBitmap*>(&rSSalBitmap));
836 const WinSalBitmap& rSalBitmap = static_cast<const WinSalBitmap&>(rSSalBitmap);
838 SalTwoRect aPosAry = rPosAry;
839 const BYTE cRed = SALCOLOR_RED( nMaskColor );
840 const BYTE cGreen = SALCOLOR_GREEN( nMaskColor );
841 const BYTE cBlue = SALCOLOR_BLUE( nMaskColor );
842 HDC hDC = mrParent.getHDC();
843 HBRUSH hMaskBrush = CreateSolidBrush( RGB( cRed, cGreen, cBlue ) );
844 HBRUSH hOldBrush = SelectBrush( hDC, hMaskBrush );
846 // WIN/WNT seems to have a minor problem mapping the correct color of the
847 // mask to the palette if we draw the DIB directly ==> draw DDB
848 if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
850 WinSalBitmap aTmp;
852 if( aTmp.Create( rSalBitmap, &mrParent ) )
853 ImplDrawBitmap( hDC, aPosAry, aTmp, false, 0x00B8074AUL );
855 else
856 ImplDrawBitmap( hDC, aPosAry, rSalBitmap, false, 0x00B8074AUL );
858 SelectBrush( hDC, hOldBrush );
859 DeleteBrush( hMaskBrush );
862 SalBitmap* WinSalGraphicsImpl::getBitmap( long nX, long nY, long nDX, long nDY )
864 SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No ::GetBitmap() from printer possible!" );
866 WinSalBitmap* pSalBitmap = nullptr;
868 nDX = labs( nDX );
869 nDY = labs( nDY );
871 HDC hDC = mrParent.getHDC();
872 HBITMAP hBmpBitmap = CreateCompatibleBitmap( hDC, nDX, nDY );
873 HDC hBmpDC = ImplGetCachedDC( CACHED_HDC_1, hBmpBitmap );
874 bool bRet;
876 bRet = BitBlt( hBmpDC, 0, 0, (int) nDX, (int) nDY, hDC, (int) nX, (int) nY, SRCCOPY ) ? TRUE : FALSE;
877 ImplReleaseCachedDC( CACHED_HDC_1 );
879 if( bRet )
881 pSalBitmap = new WinSalBitmap;
883 if( !pSalBitmap->Create( hBmpBitmap, FALSE, FALSE ) )
885 delete pSalBitmap;
886 pSalBitmap = nullptr;
889 else
891 // #124826# avoid resource leak! Happens when running without desktop access (remote desktop, service, may be screensavers)
892 DeleteBitmap( hBmpBitmap );
895 return pSalBitmap;
898 SalColor WinSalGraphicsImpl::getPixel( long nX, long nY )
900 COLORREF aWinCol = ::GetPixel( mrParent.getHDC(), (int) nX, (int) nY );
902 if ( CLR_INVALID == aWinCol )
903 return MAKE_SALCOLOR( 0, 0, 0 );
904 else
905 return MAKE_SALCOLOR( GetRValue( aWinCol ),
906 GetGValue( aWinCol ),
907 GetBValue( aWinCol ) );
910 void WinSalGraphicsImpl::invert( long nX, long nY, long nWidth, long nHeight, SalInvert nFlags )
912 if ( nFlags & SalInvert::TrackFrame )
914 HPEN hDotPen = CreatePen( PS_DOT, 0, 0 );
915 HPEN hOldPen = SelectPen( mrParent.getHDC(), hDotPen );
916 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), GetStockBrush( NULL_BRUSH ) );
917 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
919 WIN_Rectangle( mrParent.getHDC(), (int)nX, (int)nY, (int)(nX+nWidth), (int)(nY+nHeight) );
921 SetROP2( mrParent.getHDC(), nOldROP );
922 SelectPen( mrParent.getHDC(), hOldPen );
923 SelectBrush( mrParent.getHDC(), hOldBrush );
924 DeletePen( hDotPen );
926 else if ( nFlags & SalInvert::N50 )
928 SalData* pSalData = GetSalData();
929 if ( !pSalData->mh50Brush )
931 if ( !pSalData->mh50Bmp )
932 pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
933 pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
936 COLORREF nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
937 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), pSalData->mh50Brush );
938 PatBlt( mrParent.getHDC(), nX, nY, nWidth, nHeight, PATINVERT );
939 ::SetTextColor( mrParent.getHDC(), nOldTextColor );
940 SelectBrush( mrParent.getHDC(), hOldBrush );
942 else
944 RECT aRect;
945 aRect.left = (int)nX;
946 aRect.top = (int)nY;
947 aRect.right = (int)nX+nWidth;
948 aRect.bottom = (int)nY+nHeight;
949 ::InvertRect( mrParent.getHDC(), &aRect );
953 void WinSalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nSalFlags )
955 HPEN hPen;
956 HPEN hOldPen;
957 HBRUSH hBrush;
958 HBRUSH hOldBrush = nullptr;
959 COLORREF nOldTextColor RGB(0,0,0);
960 int nOldROP = SetROP2( mrParent.getHDC(), R2_NOT );
962 if ( nSalFlags & SalInvert::TrackFrame )
963 hPen = CreatePen( PS_DOT, 0, 0 );
964 else
967 if ( nSalFlags & SalInvert::N50 )
969 SalData* pSalData = GetSalData();
970 if ( !pSalData->mh50Brush )
972 if ( !pSalData->mh50Bmp )
973 pSalData->mh50Bmp = ImplLoadSalBitmap( SAL_RESID_BITMAP_50 );
974 pSalData->mh50Brush = CreatePatternBrush( pSalData->mh50Bmp );
977 hBrush = pSalData->mh50Brush;
979 else
980 hBrush = GetStockBrush( BLACK_BRUSH );
982 hPen = GetStockPen( NULL_PEN );
983 nOldTextColor = ::SetTextColor( mrParent.getHDC(), 0 );
984 hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
986 hOldPen = SelectPen( mrParent.getHDC(), hPen );
988 POINT const * pWinPtAry;
989 // for NT, we can handover the array directly
990 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
992 pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
993 // for Windows 95 and its maximum number of points
994 if ( nSalFlags & SalInvert::TrackFrame )
996 if ( !Polyline( mrParent.getHDC(), pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
997 Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
999 else
1001 if ( !Polygon( mrParent.getHDC(), pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
1002 Polygon( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
1005 SetROP2( mrParent.getHDC(), nOldROP );
1006 SelectPen( mrParent.getHDC(), hOldPen );
1008 if ( nSalFlags & SalInvert::TrackFrame )
1009 DeletePen( hPen );
1010 else
1012 ::SetTextColor( mrParent.getHDC(), nOldTextColor );
1013 SelectBrush( mrParent.getHDC(), hOldBrush );
1017 sal_uInt16 WinSalGraphicsImpl::GetBitCount() const
1019 return (sal_uInt16)GetDeviceCaps( mrParent.getHDC(), BITSPIXEL );
1022 long WinSalGraphicsImpl::GetGraphicsWidth() const
1024 if( mrParent.gethWnd() && IsWindow( mrParent.gethWnd() ) )
1026 WinSalFrame* pFrame = GetWindowPtr( mrParent.gethWnd() );
1027 if( pFrame )
1029 if( pFrame->maGeometry.nWidth )
1030 return pFrame->maGeometry.nWidth;
1031 else
1033 // TODO: perhaps not needed, maGeometry should always be up-to-date
1034 RECT aRect;
1035 GetClientRect( mrParent.gethWnd(), &aRect );
1036 return aRect.right;
1041 return 0;
1044 void WinSalGraphicsImpl::ResetClipRegion()
1046 if ( mrParent.mhRegion )
1048 DeleteRegion( mrParent.mhRegion );
1049 mrParent.mhRegion = nullptr;
1052 SelectClipRgn( mrParent.getHDC(), nullptr );
1055 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolygon& rCandidate)
1057 if(rCandidate.areControlPointsUsed())
1059 return false;
1062 const sal_uInt32 nPointCount(rCandidate.count());
1064 if(nPointCount < 2)
1066 return true;
1069 const sal_uInt32 nEdgeCount(rCandidate.isClosed() ? nPointCount + 1 : nPointCount);
1070 basegfx::B2DPoint aLast(rCandidate.getB2DPoint(0));
1072 for(sal_uInt32 a(1); a < nEdgeCount; a++)
1074 const sal_uInt32 nNextIndex(a % nPointCount);
1075 const basegfx::B2DPoint aCurrent(rCandidate.getB2DPoint(nNextIndex));
1077 if(!basegfx::fTools::equal(aLast.getX(), aCurrent.getX()) && !basegfx::fTools::equal(aLast.getY(), aCurrent.getY()))
1079 return false;
1082 aLast = aCurrent;
1085 return true;
1088 static bool containsOnlyHorizontalAndVerticalEdges(const basegfx::B2DPolyPolygon& rCandidate)
1090 if(rCandidate.areControlPointsUsed())
1092 return false;
1095 for(sal_uInt32 a(0); a < rCandidate.count(); a++)
1097 if(!containsOnlyHorizontalAndVerticalEdges(rCandidate.getB2DPolygon(a)))
1099 return false;
1103 return true;
1106 bool WinSalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip )
1108 if ( mrParent.mhRegion )
1110 DeleteRegion( mrParent.mhRegion );
1111 mrParent.mhRegion = nullptr;
1114 bool bUsePolygon(i_rClip.HasPolyPolygonOrB2DPolyPolygon());
1115 static bool bTryToAvoidPolygon(true);
1117 // #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve
1118 // and only contains horizontal/vertical edges. In that case, use the fallback
1119 // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do
1120 // the correct polygon-to-RegionBand transformation.
1121 // Background is that when using the same Rectangle as rectangle or as Polygon
1122 // clip region will lead to different results; the polygon-based one will be
1123 // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This
1124 // again is because of the polygon-nature and it's classic handling when filling.
1125 // This also means that all cases which use a 'true' polygon-based incarnation of
1126 // a vcl::Region should know what they do - it may lead to repaint errors.
1127 if(bUsePolygon && bTryToAvoidPolygon)
1129 const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1131 if(!aPolyPolygon.areControlPointsUsed())
1133 if(containsOnlyHorizontalAndVerticalEdges(aPolyPolygon))
1135 bUsePolygon = false;
1140 if(bUsePolygon)
1142 // #i122149# check the comment above to know that this may lead to potential repaint
1143 // problems. It may be solved (if needed) by scaling the polygon by one in X
1144 // and Y. Currently the workaround to only use it if really unavoidable will
1145 // solve most cases. When someone is really using polygon-based Regions he
1146 // should know what he is doing.
1147 // Added code to do that scaling to check if it works, testing it.
1148 const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
1149 const sal_uInt32 nCount(aPolyPolygon.count());
1151 if( nCount )
1153 std::vector< POINT > aPolyPoints;
1154 aPolyPoints.reserve( 1024 );
1155 std::vector< INT > aPolyCounts( nCount, 0 );
1156 basegfx::B2DHomMatrix aExpand;
1157 sal_uInt32 nTargetCount(0);
1158 static bool bExpandByOneInXandY(true);
1160 if(bExpandByOneInXandY)
1162 const basegfx::B2DRange aRangeS(aPolyPolygon.getB2DRange());
1163 const basegfx::B2DRange aRangeT(aRangeS.getMinimum(), aRangeS.getMaximum() + basegfx::B2DTuple(1.0, 1.0));
1164 aExpand = basegfx::B2DHomMatrix(basegfx::tools::createSourceRangeTargetRangeTransform(aRangeS, aRangeT));
1167 for(sal_uInt32 a(0); a < nCount; a++)
1169 const basegfx::B2DPolygon aPoly(
1170 basegfx::tools::adaptiveSubdivideByDistance(
1171 aPolyPolygon.getB2DPolygon(a),
1172 1));
1173 const sal_uInt32 nPoints(aPoly.count());
1175 // tdf#40863 For CustomShapes there is a hack (see
1176 // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons
1177 // with a single point in top-left and bottom-right corner
1178 // of the BoundRect to be able to determine the correct BoundRect
1179 // in the slideshow. Unfortunately, CreatePolyPolygonRgn below
1180 // fails with polygons containing a single pixel, so clipping is
1181 // lost. For now, use only polygons with more than two points - the
1182 // ones that may have an area.
1183 // Note: polygons with one point which are curves may have an area,
1184 // but the polygon is already subdivided here, so no need to test
1185 // this.
1186 if(nPoints > 2)
1188 aPolyCounts[nTargetCount] = nPoints;
1189 nTargetCount++;
1191 for( sal_uInt32 b = 0; b < nPoints; b++ )
1193 basegfx::B2DPoint aPt(aPoly.getB2DPoint(b));
1195 if(bExpandByOneInXandY)
1197 aPt = aExpand * aPt;
1200 POINT aPOINT;
1201 // #i122149# do correct rounding
1202 aPOINT.x = basegfx::fround(aPt.getX());
1203 aPOINT.y = basegfx::fround(aPt.getY());
1204 aPolyPoints.push_back( aPOINT );
1209 if(nTargetCount)
1211 mrParent.mhRegion = CreatePolyPolygonRgn( &aPolyPoints[0], &aPolyCounts[0], nTargetCount, ALTERNATE );
1215 else
1217 RectangleVector aRectangles;
1218 i_rClip.GetRegionRectangles(aRectangles);
1220 sal_uLong nRectBufSize = sizeof(RECT)*aRectangles.size();
1221 if ( aRectangles.size() < SAL_CLIPRECT_COUNT )
1223 if ( !mrParent.mpStdClipRgnData )
1224 mrParent.mpStdClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+(SAL_CLIPRECT_COUNT*sizeof(RECT))]);
1225 mrParent.mpClipRgnData = mrParent.mpStdClipRgnData;
1227 else
1228 mrParent.mpClipRgnData = reinterpret_cast<RGNDATA*>(new BYTE[sizeof(RGNDATA)-1+nRectBufSize]);
1229 mrParent.mpClipRgnData->rdh.dwSize = sizeof( RGNDATAHEADER );
1230 mrParent.mpClipRgnData->rdh.iType = RDH_RECTANGLES;
1231 mrParent.mpClipRgnData->rdh.nCount = aRectangles.size();
1232 mrParent.mpClipRgnData->rdh.nRgnSize = nRectBufSize;
1233 RECT* pBoundRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1234 SetRectEmpty( pBoundRect );
1235 RECT* pNextClipRect = reinterpret_cast<RECT*>(&(mrParent.mpClipRgnData->Buffer));
1236 bool bFirstClipRect = true;
1238 for(RectangleVector::const_iterator aRectIter(aRectangles.begin()); aRectIter != aRectangles.end(); ++aRectIter)
1240 const long nW(aRectIter->GetWidth());
1241 const long nH(aRectIter->GetHeight());
1243 if(nW && nH)
1245 const long nRight(aRectIter->Left() + nW);
1246 const long nBottom(aRectIter->Top() + nH);
1248 if(bFirstClipRect)
1250 pBoundRect->left = aRectIter->Left();
1251 pBoundRect->top = aRectIter->Top();
1252 pBoundRect->right = nRight;
1253 pBoundRect->bottom = nBottom;
1254 bFirstClipRect = false;
1256 else
1258 if(aRectIter->Left() < pBoundRect->left)
1260 pBoundRect->left = (int)aRectIter->Left();
1263 if(aRectIter->Top() < pBoundRect->top)
1265 pBoundRect->top = (int)aRectIter->Top();
1268 if(nRight > pBoundRect->right)
1270 pBoundRect->right = (int)nRight;
1273 if(nBottom > pBoundRect->bottom)
1275 pBoundRect->bottom = (int)nBottom;
1279 pNextClipRect->left = (int)aRectIter->Left();
1280 pNextClipRect->top = (int)aRectIter->Top();
1281 pNextClipRect->right = (int)nRight;
1282 pNextClipRect->bottom = (int)nBottom;
1283 pNextClipRect++;
1285 else
1287 mrParent.mpClipRgnData->rdh.nCount--;
1288 mrParent.mpClipRgnData->rdh.nRgnSize -= sizeof( RECT );
1292 // create clip region from ClipRgnData
1293 if(0 == mrParent.mpClipRgnData->rdh.nCount)
1295 // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given
1296 // that contains no polygons or only empty ones (no width/height). This is
1297 // perfectly fine and we are done, except setting it (see end of method)
1299 else if(1 == mrParent.mpClipRgnData->rdh.nCount)
1301 RECT* pRect = &(mrParent.mpClipRgnData->rdh.rcBound);
1302 mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top,
1303 pRect->right, pRect->bottom );
1305 else if(mrParent.mpClipRgnData->rdh.nCount > 1)
1307 sal_uLong nSize = mrParent.mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
1308 mrParent.mhRegion = ExtCreateRegion( nullptr, nSize, mrParent.mpClipRgnData );
1310 // if ExtCreateRegion(...) is not supported
1311 if( !mrParent.mhRegion )
1313 RGNDATAHEADER const & pHeader = mrParent.mpClipRgnData->rdh;
1315 if( pHeader.nCount )
1317 RECT* pRect = reinterpret_cast<RECT*>(mrParent.mpClipRgnData->Buffer);
1318 mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
1319 pRect++;
1321 for( sal_uLong n = 1; n < pHeader.nCount; n++, pRect++ )
1323 HRGN hRgn = CreateRectRgn( pRect->left, pRect->top, pRect->right, pRect->bottom );
1324 CombineRgn( mrParent.mhRegion, mrParent.mhRegion, hRgn, RGN_OR );
1325 DeleteRegion( hRgn );
1330 if ( mrParent.mpClipRgnData != mrParent.mpStdClipRgnData )
1331 delete [] reinterpret_cast<BYTE*>(mrParent.mpClipRgnData);
1335 if( mrParent.mhRegion )
1337 SelectClipRgn( mrParent.getHDC(), mrParent.mhRegion );
1339 // debug code if you want to check range of the newly applied ClipRegion
1340 //RECT aBound;
1341 //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
1343 //bool bBla = true;
1345 else
1347 // #i123585# See above, this is a valid case, execute it
1348 SelectClipRgn( mrParent.getHDC(), nullptr );
1351 // #i123585# retval no longer dependent of mrParent.mhRegion, see TaskId comments above
1352 return true;
1355 void WinSalGraphicsImpl::SetLineColor()
1357 // create and select new pen
1358 HPEN hNewPen = GetStockPen( NULL_PEN );
1359 HPEN hOldPen = SelectPen( mrParent.getHDC(), hNewPen );
1361 // destroy or save old pen
1362 if ( mhPen )
1364 if ( !mbStockPen )
1365 DeletePen( mhPen );
1367 else
1368 mrParent.mhDefPen = hOldPen;
1370 // set new data
1371 mhPen = hNewPen;
1372 mbPen = FALSE;
1373 mbStockPen = TRUE;
1376 void WinSalGraphicsImpl::SetLineColor( SalColor nSalColor )
1378 maLineColor = nSalColor;
1379 COLORREF nPenColor = PALETTERGB( SALCOLOR_RED( nSalColor ),
1380 SALCOLOR_GREEN( nSalColor ),
1381 SALCOLOR_BLUE( nSalColor ) );
1382 HPEN hNewPen = nullptr;
1383 bool bStockPen = FALSE;
1385 // search for stock pen (only screen, because printer have problems,
1386 // when we use stock objects)
1387 if ( !mrParent.isPrinter() )
1389 SalData* pSalData = GetSalData();
1390 for ( sal_uInt16 i = 0; i < pSalData->mnStockPenCount; i++ )
1392 if ( nPenColor == pSalData->maStockPenColorAry[i] )
1394 hNewPen = pSalData->mhStockPenAry[i];
1395 bStockPen = TRUE;
1396 break;
1401 // create new pen
1402 if ( !hNewPen )
1404 if ( !mrParent.isPrinter() )
1406 if ( GetSalData()->mhDitherPal && ImplIsSysColorEntry( nSalColor ) )
1407 nPenColor = PALRGB_TO_RGB( nPenColor );
1410 hNewPen = CreatePen( PS_SOLID, mrParent.mnPenWidth, nPenColor );
1411 bStockPen = FALSE;
1414 // select new pen
1415 HPEN hOldPen = SelectPen( mrParent.getHDC(), hNewPen );
1417 // destroy or save old pen
1418 if ( mhPen )
1420 if ( !mbStockPen )
1421 DeletePen( mhPen );
1423 else
1424 mrParent.mhDefPen = hOldPen;
1426 // set new data
1427 mnPenColor = nPenColor;
1428 mhPen = hNewPen;
1429 mbPen = TRUE;
1430 mbStockPen = bStockPen;
1433 void WinSalGraphicsImpl::SetFillColor()
1435 // create and select new brush
1436 HBRUSH hNewBrush = GetStockBrush( NULL_BRUSH );
1437 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hNewBrush );
1439 // destroy or save old brush
1440 if ( mhBrush )
1442 if ( !mbStockBrush )
1443 DeleteBrush( mhBrush );
1445 else
1446 mrParent.mhDefBrush = hOldBrush;
1448 // set new data
1449 mhBrush = hNewBrush;
1450 mbBrush = FALSE;
1451 mbStockBrush = TRUE;
1454 void WinSalGraphicsImpl::SetFillColor( SalColor nSalColor )
1456 maFillColor = nSalColor;
1457 SalData* pSalData = GetSalData();
1458 BYTE nRed = SALCOLOR_RED( nSalColor );
1459 BYTE nGreen = SALCOLOR_GREEN( nSalColor );
1460 BYTE nBlue = SALCOLOR_BLUE( nSalColor );
1461 COLORREF nBrushColor = PALETTERGB( nRed, nGreen, nBlue );
1462 HBRUSH hNewBrush = nullptr;
1463 bool bStockBrush = FALSE;
1465 // search for stock brush (only screen, because printer have problems,
1466 // when we use stock objects)
1467 if ( !mrParent.isPrinter() )
1469 for ( sal_uInt16 i = 0; i < pSalData->mnStockBrushCount; i++ )
1471 if ( nBrushColor == pSalData->maStockBrushColorAry[ i ] )
1473 hNewBrush = pSalData->mhStockBrushAry[i];
1474 bStockBrush = TRUE;
1475 break;
1480 // create new brush
1481 if ( !hNewBrush )
1483 if ( mrParent.isPrinter() || !pSalData->mhDitherDIB )
1484 hNewBrush = CreateSolidBrush( nBrushColor );
1485 else
1487 if ( 24 == reinterpret_cast<BITMAPINFOHEADER*>(pSalData->mpDitherDIB)->biBitCount )
1489 BYTE* pTmp = pSalData->mpDitherDIBData;
1490 long* pDitherDiff = pSalData->mpDitherDiff;
1491 BYTE* pDitherLow = pSalData->mpDitherLow;
1492 BYTE* pDitherHigh = pSalData->mpDitherHigh;
1494 for( long nY = 0L; nY < 8L; nY++ )
1496 for( long nX = 0L; nX < 8L; nX++ )
1498 const long nThres = aOrdDither16Bit[ nY ][ nX ];
1499 *pTmp++ = DMAP( nBlue, nThres );
1500 *pTmp++ = DMAP( nGreen, nThres );
1501 *pTmp++ = DMAP( nRed, nThres );
1505 hNewBrush = CreateDIBPatternBrush( pSalData->mhDitherDIB, DIB_RGB_COLORS );
1507 else if ( ImplIsSysColorEntry( nSalColor ) )
1509 nBrushColor = PALRGB_TO_RGB( nBrushColor );
1510 hNewBrush = CreateSolidBrush( nBrushColor );
1512 else if ( ImplIsPaletteEntry( nRed, nGreen, nBlue ) )
1513 hNewBrush = CreateSolidBrush( nBrushColor );
1514 else
1516 BYTE* pTmp = pSalData->mpDitherDIBData;
1517 long* pDitherDiff = pSalData->mpDitherDiff;
1518 BYTE* pDitherLow = pSalData->mpDitherLow;
1519 BYTE* pDitherHigh = pSalData->mpDitherHigh;
1521 for ( long nY = 0L; nY < 8L; nY++ )
1523 for ( long nX = 0L; nX < 8L; nX++ )
1525 const long nThres = aOrdDither8Bit[ nY ][ nX ];
1526 *pTmp = DMAP( nRed, nThres ) + DMAP( nGreen, nThres ) * 6 + DMAP( nBlue, nThres ) * 36;
1527 pTmp++;
1531 hNewBrush = CreateDIBPatternBrush( pSalData->mhDitherDIB, DIB_PAL_COLORS );
1535 bStockBrush = FALSE;
1538 // select new brush
1539 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hNewBrush );
1541 // destroy or save old brush
1542 if ( mhBrush )
1544 if ( !mbStockBrush )
1545 DeleteBrush( mhBrush );
1547 else
1548 mrParent.mhDefBrush = hOldBrush;
1550 // set new data
1551 mnBrushColor = nBrushColor;
1552 mhBrush = hNewBrush;
1553 mbBrush = TRUE;
1554 mbStockBrush = bStockBrush;
1557 void WinSalGraphicsImpl::SetXORMode( bool bSet)
1559 mbXORMode = bSet;
1560 ::SetROP2( mrParent.getHDC(), bSet ? R2_XORPEN : R2_COPYPEN );
1563 void WinSalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor )
1565 SetLineColor( ImplGetROPSalColor( nROPColor ) );
1568 void WinSalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor )
1570 SetFillColor( ImplGetROPSalColor( nROPColor ) );
1573 void WinSalGraphicsImpl::drawPixel( long nX, long nY )
1575 if ( mbXORMode )
1577 HBRUSH hBrush = CreateSolidBrush( mnPenColor );
1578 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
1579 PatBlt( mrParent.getHDC(), (int)nX, (int)nY, (int)1, (int)1, PATINVERT );
1580 SelectBrush( mrParent.getHDC(), hOldBrush );
1581 DeleteBrush( hBrush );
1583 else
1584 SetPixel( mrParent.getHDC(), (int)nX, (int)nY, mnPenColor );
1587 void WinSalGraphicsImpl::drawPixel( long nX, long nY, SalColor nSalColor )
1589 COLORREF nCol = PALETTERGB( SALCOLOR_RED( nSalColor ),
1590 SALCOLOR_GREEN( nSalColor ),
1591 SALCOLOR_BLUE( nSalColor ) );
1593 if ( !mrParent.isPrinter() &&
1594 GetSalData()->mhDitherPal &&
1595 ImplIsSysColorEntry( nSalColor ) )
1596 nCol = PALRGB_TO_RGB( nCol );
1598 if ( mbXORMode )
1600 HBRUSH hBrush = CreateSolidBrush( nCol );
1601 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
1602 PatBlt( mrParent.getHDC(), (int)nX, (int)nY, (int)1, (int)1, PATINVERT );
1603 SelectBrush( mrParent.getHDC(), hOldBrush );
1604 DeleteBrush( hBrush );
1606 else
1607 ::SetPixel( mrParent.getHDC(), (int)nX, (int)nY, nCol );
1610 void WinSalGraphicsImpl::drawLine( long nX1, long nY1, long nX2, long nY2 )
1612 MoveToEx( mrParent.getHDC(), (int)nX1, (int)nY1, nullptr );
1614 // we must paint the endpoint
1615 int bPaintEnd = TRUE;
1616 if ( nX1 == nX2 )
1618 bPaintEnd = FALSE;
1619 if ( nY1 <= nY2 )
1620 nY2++;
1621 else
1622 nY2--;
1624 if ( nY1 == nY2 )
1626 bPaintEnd = FALSE;
1627 if ( nX1 <= nX2 )
1628 nX2++;
1629 else
1630 nX2--;
1633 LineTo( mrParent.getHDC(), (int)nX2, (int)nY2 );
1635 if ( bPaintEnd && !mrParent.isPrinter() )
1637 if ( mbXORMode )
1639 HBRUSH hBrush = CreateSolidBrush( mnPenColor );
1640 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
1641 PatBlt( mrParent.getHDC(), (int)nX2, (int)nY2, (int)1, (int)1, PATINVERT );
1642 SelectBrush( mrParent.getHDC(), hOldBrush );
1643 DeleteBrush( hBrush );
1645 else
1646 SetPixel( mrParent.getHDC(), (int)nX2, (int)nY2, mnPenColor );
1650 void WinSalGraphicsImpl::drawRect( long nX, long nY, long nWidth, long nHeight )
1652 if ( !mbPen )
1654 if ( !mrParent.isPrinter() )
1656 PatBlt( mrParent.getHDC(), (int)nX, (int)nY, (int)nWidth, (int)nHeight,
1657 mbXORMode ? PATINVERT : PATCOPY );
1659 else
1661 RECT aWinRect;
1662 aWinRect.left = nX;
1663 aWinRect.top = nY;
1664 aWinRect.right = nX+nWidth;
1665 aWinRect.bottom = nY+nHeight;
1666 ::FillRect( mrParent.getHDC(), &aWinRect, mhBrush );
1669 else
1670 WIN_Rectangle( mrParent.getHDC(), (int)nX, (int)nY, (int)(nX+nWidth), (int)(nY+nHeight) );
1673 void WinSalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, SalPoint* pPtAry )
1675 // for NT, we can handover the array directly
1676 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1678 POINT* pWinPtAry = reinterpret_cast<POINT*>(pPtAry);
1680 // we assume there are at least 2 points (Polyline requires at least 2 point, see MSDN)
1681 // we must paint the endpoint for last line
1682 BOOL bPaintEnd = TRUE;
1683 if ( pWinPtAry[nPoints-2].x == pWinPtAry[nPoints-1].x )
1685 bPaintEnd = FALSE;
1686 if ( pWinPtAry[nPoints-2].y <= pWinPtAry[nPoints-1].y )
1687 pWinPtAry[nPoints-1].y++;
1688 else
1689 pWinPtAry[nPoints-1].y--;
1691 if ( pWinPtAry[nPoints-2].y == pWinPtAry[nPoints-1].y )
1693 bPaintEnd = FALSE;
1694 if ( pWinPtAry[nPoints-2].x <= pWinPtAry[nPoints-1].x )
1695 pWinPtAry[nPoints-1].x++;
1696 else
1697 pWinPtAry[nPoints-1].x--;
1700 // for Windows 95 and its maximum number of points
1701 if ( !Polyline( mrParent.getHDC(), pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
1702 Polyline( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
1704 if ( bPaintEnd && !mrParent.isPrinter() )
1706 if ( mbXORMode )
1708 HBRUSH hBrush = CreateSolidBrush( mnPenColor );
1709 HBRUSH hOldBrush = SelectBrush( mrParent.getHDC(), hBrush );
1710 PatBlt( mrParent.getHDC(), (int)(pWinPtAry[nPoints-1].x), (int)(pWinPtAry[nPoints-1].y), (int)1, (int)1, PATINVERT );
1711 SelectBrush( mrParent.getHDC(), hOldBrush );
1712 DeleteBrush( hBrush );
1714 else
1715 SetPixel( mrParent.getHDC(), (int)(pWinPtAry[nPoints-1].x), (int)(pWinPtAry[nPoints-1].y), mnPenColor );
1719 void WinSalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry )
1721 // for NT, we can handover the array directly
1722 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1724 POINT const * pWinPtAry = reinterpret_cast<POINT const *>(pPtAry);
1725 // for Windows 95 and its maximum number of points
1726 if ( !Polygon( mrParent.getHDC(), pWinPtAry, (int)nPoints ) && (nPoints > MAX_64KSALPOINTS) )
1727 Polygon( mrParent.getHDC(), pWinPtAry, MAX_64KSALPOINTS );
1730 void WinSalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1731 PCONSTSALPOINT* pPtAry )
1733 UINT aWinPointAry[SAL_POLYPOLYCOUNT_STACKBUF];
1734 UINT* pWinPointAry;
1735 UINT nPolyPolyPoints = 0;
1736 UINT nPoints;
1737 UINT i;
1739 if ( nPoly <= SAL_POLYPOLYCOUNT_STACKBUF )
1740 pWinPointAry = aWinPointAry;
1741 else
1742 pWinPointAry = new UINT[nPoly];
1744 for ( i = 0; i < (UINT)nPoly; i++ )
1746 nPoints = (UINT)pPoints[i]+1;
1747 pWinPointAry[i] = nPoints;
1748 nPolyPolyPoints += nPoints;
1751 POINT aWinPointAryAry[SAL_POLYPOLYPOINTS_STACKBUF];
1752 POINT* pWinPointAryAry;
1753 if ( nPolyPolyPoints <= SAL_POLYPOLYPOINTS_STACKBUF )
1754 pWinPointAryAry = aWinPointAryAry;
1755 else
1756 pWinPointAryAry = new POINT[nPolyPolyPoints];
1757 // for NT, we can handover the array directly
1758 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1759 UINT n = 0;
1760 for ( i = 0; i < (UINT)nPoly; i++ )
1762 nPoints = pWinPointAry[i];
1763 const SalPoint* pPolyAry = pPtAry[i];
1764 memcpy( pWinPointAryAry+n, pPolyAry, (nPoints-1)*sizeof(POINT) );
1765 pWinPointAryAry[n+nPoints-1] = pWinPointAryAry[n];
1766 n += nPoints;
1769 if ( !PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), (UINT)nPoly ) &&
1770 (nPolyPolyPoints > MAX_64KSALPOINTS) )
1772 nPolyPolyPoints = 0;
1773 nPoly = 0;
1776 nPolyPolyPoints += pWinPointAry[(UINT)nPoly];
1777 nPoly++;
1779 while ( nPolyPolyPoints < MAX_64KSALPOINTS );
1780 nPoly--;
1781 if ( pWinPointAry[(UINT)nPoly] > MAX_64KSALPOINTS )
1782 pWinPointAry[(UINT)nPoly] = MAX_64KSALPOINTS;
1783 if ( nPoly == 1 )
1784 Polygon( mrParent.getHDC(), pWinPointAryAry, *pWinPointAry );
1785 else
1786 PolyPolygon( mrParent.getHDC(), pWinPointAryAry, reinterpret_cast<int*>(pWinPointAry), nPoly );
1789 if ( pWinPointAry != aWinPointAry )
1790 delete [] pWinPointAry;
1791 if ( pWinPointAryAry != aWinPointAryAry )
1792 delete [] pWinPointAryAry;
1795 bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
1797 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1799 ImplRenderPath( mrParent.getHDC(), nPoints, pPtAry, pFlgAry );
1801 return true;
1804 bool WinSalGraphicsImpl::drawPolygonBezier( sal_uInt32 nPoints, const SalPoint* pPtAry, const PolyFlags* pFlgAry )
1806 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1808 POINT aStackAry1[SAL_POLY_STACKBUF];
1809 BYTE aStackAry2[SAL_POLY_STACKBUF];
1810 POINT* pWinPointAry;
1811 BYTE* pWinFlagAry;
1812 if( nPoints > SAL_POLY_STACKBUF )
1814 pWinPointAry = new POINT[ nPoints ];
1815 pWinFlagAry = new BYTE[ nPoints ];
1817 else
1819 pWinPointAry = aStackAry1;
1820 pWinFlagAry = aStackAry2;
1823 sal_uInt32 nPoints_i32(nPoints);
1824 ImplPreparePolyDraw(true, 1, &nPoints_i32, &pPtAry, &pFlgAry, pWinPointAry, pWinFlagAry);
1826 bool bRet( false );
1828 if( BeginPath( mrParent.getHDC() ) )
1830 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nPoints);
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 bool WinSalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32 nPoly, const sal_uInt32* pPoints,
1849 const SalPoint* const* pPtAry, const PolyFlags* const* pFlgAry )
1851 static_assert( sizeof( POINT ) == sizeof( SalPoint ), "must be the same size" );
1853 sal_uLong nCurrPoly, nTotalPoints;
1854 const sal_uInt32* pCurrPoints = pPoints;
1855 for( nCurrPoly=0, nTotalPoints=0; nCurrPoly<nPoly; ++nCurrPoly )
1856 nTotalPoints += *pCurrPoints++;
1858 POINT aStackAry1[SAL_POLY_STACKBUF];
1859 BYTE aStackAry2[SAL_POLY_STACKBUF];
1860 POINT* pWinPointAry;
1861 BYTE* pWinFlagAry;
1862 if( nTotalPoints > SAL_POLY_STACKBUF )
1864 pWinPointAry = new POINT[ nTotalPoints ];
1865 pWinFlagAry = new BYTE[ nTotalPoints ];
1867 else
1869 pWinPointAry = aStackAry1;
1870 pWinFlagAry = aStackAry2;
1873 ImplPreparePolyDraw(true, nPoly, pPoints, pPtAry, pFlgAry, pWinPointAry, pWinFlagAry);
1875 bool bRet( false );
1877 if( BeginPath( mrParent.getHDC() ) )
1879 PolyDraw(mrParent.getHDC(), pWinPointAry, pWinFlagAry, nTotalPoints);
1881 if( EndPath( mrParent.getHDC() ) )
1883 if( StrokeAndFillPath( mrParent.getHDC() ) )
1884 bRet = true;
1888 if( pWinPointAry != aStackAry1 )
1890 delete [] pWinPointAry;
1891 delete [] pWinFlagAry;
1894 return bRet;
1897 void impAddB2DPolygonToGDIPlusGraphicsPathReal(
1898 Gdiplus::GraphicsPath& rGraphicsPath,
1899 const basegfx::B2DPolygon& rPolygon,
1900 bool bNoLineJoin)
1902 sal_uInt32 nCount(rPolygon.count());
1904 if(nCount)
1906 const sal_uInt32 nEdgeCount(rPolygon.isClosed() ? nCount : nCount - 1);
1907 const bool bControls(rPolygon.areControlPointsUsed());
1908 basegfx::B2DPoint aCurr(rPolygon.getB2DPoint(0));
1910 if(nEdgeCount)
1912 for(sal_uInt32 a(0); a < nEdgeCount; a++)
1914 const sal_uInt32 nNextIndex((a + 1) % nCount);
1915 const basegfx::B2DPoint aNext(rPolygon.getB2DPoint(nNextIndex));
1916 const bool b1stControlPointUsed(bControls && rPolygon.isNextControlPointUsed(a));
1917 const bool b2ndControlPointUsed(bControls && rPolygon.isPrevControlPointUsed(nNextIndex));
1919 if(b1stControlPointUsed || b2ndControlPointUsed)
1921 basegfx::B2DPoint aCa(rPolygon.getNextControlPoint(a));
1922 basegfx::B2DPoint aCb(rPolygon.getPrevControlPoint(nNextIndex));
1924 // tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines
1925 // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has
1926 // no 1st or 2nd control point, despite that these are mathematicaly correct definitions
1927 // (basegfx can handle that).
1928 // Caution: This error (and it's correction) might be necessary for other graphical
1929 // sub-systems in a similar way.
1930 // tdf#101026 The 1st attempt to create a mathematically correct replacement control
1931 // vector was wrong. Best alternative is one as close as possible which means short.
1932 if(!b1stControlPointUsed)
1934 aCa = aCurr + ((aCb - aCurr) * 0.0005);
1936 else if(!b2ndControlPointUsed)
1938 aCb = aNext + ((aCa - aNext) * 0.0005);
1941 rGraphicsPath.AddBezier(
1942 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1943 static_cast< Gdiplus::REAL >(aCa.getX()), static_cast< Gdiplus::REAL >(aCa.getY()),
1944 static_cast< Gdiplus::REAL >(aCb.getX()), static_cast< Gdiplus::REAL >(aCb.getY()),
1945 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1947 else
1949 rGraphicsPath.AddLine(
1950 static_cast< Gdiplus::REAL >(aCurr.getX()), static_cast< Gdiplus::REAL >(aCurr.getY()),
1951 static_cast< Gdiplus::REAL >(aNext.getX()), static_cast< Gdiplus::REAL >(aNext.getY()));
1954 if(a + 1 < nEdgeCount)
1956 aCurr = aNext;
1958 if(bNoLineJoin)
1960 rGraphicsPath.StartFigure();
1968 bool WinSalGraphicsImpl::drawPolyPolygon( const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency)
1970 const sal_uInt32 nCount(rPolyPolygon.count());
1972 if(mbBrush && nCount && (fTransparency >= 0.0 && fTransparency < 1.0))
1974 Gdiplus::Graphics aGraphics(mrParent.getHDC());
1975 const sal_uInt8 aTrans((sal_uInt8)255 - (sal_uInt8)basegfx::fround(fTransparency * 255.0));
1976 const Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maFillColor), SALCOLOR_GREEN(maFillColor), SALCOLOR_BLUE(maFillColor));
1977 const Gdiplus::SolidBrush aSolidBrush(aTestColor.GetValue());
1978 Gdiplus::GraphicsPath aGraphicsPath(Gdiplus::FillModeAlternate);
1980 for(sal_uInt32 a(0); a < nCount; a++)
1982 if(0 != a)
1984 // #i101491# not needed for first run
1985 aGraphicsPath.StartFigure();
1988 impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolyPolygon.getB2DPolygon(a), false);
1990 aGraphicsPath.CloseFigure();
1993 if(mrParent.getAntiAliasB2DDraw())
1995 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
1997 else
1999 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2002 if(mrParent.isPrinter())
2004 // #i121591#
2005 // Normally GdiPlus should not be used for printing at all since printers cannot
2006 // print transparent filled polygon geometry and normally this does not happen
2007 // since OutputDevice::RemoveTransparenciesFromMetaFile is used as preparation
2008 // and no transparent parts should remain for printing. But this can be overridden
2009 // by the user and thus happens. This call can only come (currently) from
2010 // OutputDevice::DrawTransparent, see comments there with the same TaskID.
2011 // If it is used, the mapping for the printer is wrong and needs to be corrected. I
2012 // checked that there is *no* transformation set and estimated that a stable factor
2013 // dependent of the printer's DPI is used. Create and set a transformation here to
2014 // correct this.
2015 const Gdiplus::REAL aDpiX(aGraphics.GetDpiX());
2016 const Gdiplus::REAL aDpiY(aGraphics.GetDpiY());
2018 aGraphics.ResetTransform();
2019 aGraphics.ScaleTransform(Gdiplus::REAL(100.0) / aDpiX, Gdiplus::REAL(100.0) / aDpiY, Gdiplus::MatrixOrderAppend);
2022 aGraphics.FillPath(&aSolidBrush, &aGraphicsPath);
2025 return true;
2028 bool WinSalGraphicsImpl::drawPolyLine(
2029 const basegfx::B2DPolygon& rPolygon,
2030 double fTransparency,
2031 const basegfx::B2DVector& rLineWidths,
2032 basegfx::B2DLineJoin eLineJoin,
2033 css::drawing::LineCap eLineCap,
2034 double fMiterMinimumAngle)
2036 const sal_uInt32 nCount(rPolygon.count());
2038 if(mbPen && nCount)
2040 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2041 const sal_uInt8 aTrans = (sal_uInt8)basegfx::fround( 255 * (1.0 - fTransparency) );
2042 const Gdiplus::Color aTestColor(aTrans, SALCOLOR_RED(maLineColor), SALCOLOR_GREEN(maLineColor), SALCOLOR_BLUE(maLineColor));
2043 Gdiplus::Pen aPen(aTestColor.GetValue(), Gdiplus::REAL(rLineWidths.getX()));
2044 Gdiplus::GraphicsPath aGraphicsPath(Gdiplus::FillModeAlternate);
2045 bool bNoLineJoin(false);
2047 switch(eLineJoin)
2049 case basegfx::B2DLineJoin::NONE :
2051 if(basegfx::fTools::more(rLineWidths.getX(), 0.0))
2053 bNoLineJoin = true;
2055 break;
2057 case basegfx::B2DLineJoin::Bevel :
2059 aPen.SetLineJoin(Gdiplus::LineJoinBevel);
2060 break;
2062 case basegfx::B2DLineJoin::Miter :
2064 const Gdiplus::REAL aMiterLimit(1.0/sin(fMiterMinimumAngle/2.0));
2066 aPen.SetMiterLimit(aMiterLimit);
2067 // tdf#99165 MS's LineJoinMiter creates non standard conform miter additional
2068 // graphics, somewhere clipped in some distance from the edge point, dependent
2069 // of MiterLimit. The more default-like option is LineJoinMiterClipped, so use
2070 // that instead
2071 aPen.SetLineJoin(Gdiplus::LineJoinMiterClipped);
2072 break;
2074 case basegfx::B2DLineJoin::Round :
2076 aPen.SetLineJoin(Gdiplus::LineJoinRound);
2077 break;
2081 switch(eLineCap)
2083 default: /*css::drawing::LineCap_BUTT*/
2085 // nothing to do
2086 break;
2088 case css::drawing::LineCap_ROUND:
2090 aPen.SetStartCap(Gdiplus::LineCapRound);
2091 aPen.SetEndCap(Gdiplus::LineCapRound);
2092 break;
2094 case css::drawing::LineCap_SQUARE:
2096 aPen.SetStartCap(Gdiplus::LineCapSquare);
2097 aPen.SetEndCap(Gdiplus::LineCapSquare);
2098 break;
2102 impAddB2DPolygonToGDIPlusGraphicsPathReal(aGraphicsPath, rPolygon, bNoLineJoin);
2104 if(rPolygon.isClosed() && !bNoLineJoin)
2106 // #i101491# needed to create the correct line joins
2107 aGraphicsPath.CloseFigure();
2110 if(mrParent.getAntiAliasB2DDraw())
2112 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
2114 else
2116 aGraphics.SetSmoothingMode(Gdiplus::SmoothingModeNone);
2119 aGraphics.DrawPath(&aPen, &aGraphicsPath);
2122 return true;
2125 void paintToGdiPlus(
2126 Gdiplus::Graphics& rGraphics,
2127 const SalTwoRect& rTR,
2128 Gdiplus::Bitmap& rBitmap)
2130 // only parts of source are used
2131 Gdiplus::PointF aDestPoints[3];
2132 Gdiplus::ImageAttributes aAttributes;
2134 // define target region as paralellogram
2135 aDestPoints[0].X = Gdiplus::REAL(rTR.mnDestX);
2136 aDestPoints[0].Y = Gdiplus::REAL(rTR.mnDestY);
2137 aDestPoints[1].X = Gdiplus::REAL(rTR.mnDestX + rTR.mnDestWidth);
2138 aDestPoints[1].Y = Gdiplus::REAL(rTR.mnDestY);
2139 aDestPoints[2].X = Gdiplus::REAL(rTR.mnDestX);
2140 aDestPoints[2].Y = Gdiplus::REAL(rTR.mnDestY + rTR.mnDestHeight);
2142 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2144 rGraphics.DrawImage(
2145 &rBitmap,
2146 aDestPoints,
2148 Gdiplus::REAL(rTR.mnSrcX),
2149 Gdiplus::REAL(rTR.mnSrcY),
2150 Gdiplus::REAL(rTR.mnSrcWidth),
2151 Gdiplus::REAL(rTR.mnSrcHeight),
2152 Gdiplus::UnitPixel,
2153 &aAttributes);
2156 void setInterpolationMode(
2157 Gdiplus::Graphics& rGraphics,
2158 long rSrcWidth,
2159 long rDestWidth,
2160 long rSrcHeight,
2161 long rDestHeight)
2163 const bool bSameWidth(rSrcWidth == rDestWidth);
2164 const bool bSameHeight(rSrcHeight == rDestHeight);
2166 if(bSameWidth && bSameHeight)
2168 #ifdef __MINGW32__
2169 //Gdiplus::InterpolationModeInvalid is missing on mingw
2170 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2171 #else
2172 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeInvalid);
2173 #endif
2175 else if(rDestWidth > rSrcWidth && rDestHeight > rSrcHeight)
2177 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2179 else if(rDestWidth < rSrcWidth && rDestHeight < rSrcHeight)
2181 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeBicubic);
2183 else
2185 rGraphics.SetInterpolationMode(Gdiplus::InterpolationModeDefault);
2189 bool WinSalGraphicsImpl::tryDrawBitmapGdiPlus(const SalTwoRect& rTR, const SalBitmap& rSrcBitmap)
2191 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2193 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2195 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2196 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap());
2198 if(aARGB.get())
2200 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2202 setInterpolationMode(
2203 aGraphics,
2204 rTR.mnSrcWidth,
2205 rTR.mnDestWidth,
2206 rTR.mnSrcHeight,
2207 rTR.mnDestHeight);
2209 paintToGdiPlus(
2210 aGraphics,
2211 rTR,
2212 *aARGB.get());
2214 return true;
2218 return false;
2221 bool WinSalGraphicsImpl::blendBitmap(
2222 const SalTwoRect&,
2223 const SalBitmap&)
2225 return false;
2228 bool WinSalGraphicsImpl::blendAlphaBitmap(
2229 const SalTwoRect&,
2230 const SalBitmap&,
2231 const SalBitmap&,
2232 const SalBitmap&)
2234 return false;
2237 bool WinSalGraphicsImpl::drawAlphaBitmap(
2238 const SalTwoRect& rTR,
2239 const SalBitmap& rSrcBitmap,
2240 const SalBitmap& rAlphaBmp)
2242 if(rTR.mnSrcWidth && rTR.mnSrcHeight && rTR.mnDestWidth && rTR.mnDestHeight)
2244 assert(dynamic_cast<const WinSalBitmap*>(&rSrcBitmap));
2245 assert(dynamic_cast<const WinSalBitmap*>(&rAlphaBmp));
2247 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSrcBitmap);
2248 const WinSalBitmap& rSalAlpha = static_cast< const WinSalBitmap& >(rAlphaBmp);
2249 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(&rSalAlpha));
2251 if(aARGB.get())
2253 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2255 setInterpolationMode(
2256 aGraphics,
2257 rTR.mnSrcWidth,
2258 rTR.mnDestWidth,
2259 rTR.mnSrcHeight,
2260 rTR.mnDestHeight);
2262 paintToGdiPlus(
2263 aGraphics,
2264 rTR,
2265 *aARGB.get());
2267 return true;
2271 return false;
2274 bool WinSalGraphicsImpl::drawTransformedBitmap(
2275 const basegfx::B2DPoint& rNull,
2276 const basegfx::B2DPoint& rX,
2277 const basegfx::B2DPoint& rY,
2278 const SalBitmap& rSourceBitmap,
2279 const SalBitmap* pAlphaBitmap)
2281 assert(dynamic_cast<const WinSalBitmap*>(&rSourceBitmap));
2282 assert(!pAlphaBitmap || dynamic_cast<const WinSalBitmap*>(pAlphaBitmap));
2284 const WinSalBitmap& rSalBitmap = static_cast< const WinSalBitmap& >(rSourceBitmap);
2285 const WinSalBitmap* pSalAlpha = static_cast< const WinSalBitmap* >(pAlphaBitmap);
2286 std::shared_ptr< Gdiplus::Bitmap > aARGB(rSalBitmap.ImplGetGdiPlusBitmap(pSalAlpha));
2288 if(aARGB.get())
2290 const long nSrcWidth(aARGB->GetWidth());
2291 const long nSrcHeight(aARGB->GetHeight());
2293 if(nSrcWidth && nSrcHeight)
2295 const long nDestWidth(basegfx::fround(basegfx::B2DVector(rX - rNull).getLength()));
2296 const long nDestHeight(basegfx::fround(basegfx::B2DVector(rY - rNull).getLength()));
2298 if(nDestWidth && nDestHeight)
2300 Gdiplus::Graphics aGraphics(mrParent.getHDC());
2301 Gdiplus::PointF aDestPoints[3];
2302 Gdiplus::ImageAttributes aAttributes;
2304 setInterpolationMode(
2305 aGraphics,
2306 nSrcWidth,
2307 nDestWidth,
2308 nSrcHeight,
2309 nDestHeight);
2311 // this mode is only capable of drawing the whole bitmap to a paralellogram
2312 aDestPoints[0].X = Gdiplus::REAL(rNull.getX());
2313 aDestPoints[0].Y = Gdiplus::REAL(rNull.getY());
2314 aDestPoints[1].X = Gdiplus::REAL(rX.getX());
2315 aDestPoints[1].Y = Gdiplus::REAL(rX.getY());
2316 aDestPoints[2].X = Gdiplus::REAL(rY.getX());
2317 aDestPoints[2].Y = Gdiplus::REAL(rY.getY());
2319 aAttributes.SetWrapMode(Gdiplus::WrapModeTileFlipXY);
2321 aGraphics.DrawImage(
2322 aARGB.get(),
2323 aDestPoints,
2325 Gdiplus::REAL(0.0),
2326 Gdiplus::REAL(0.0),
2327 Gdiplus::REAL(nSrcWidth),
2328 Gdiplus::REAL(nSrcHeight),
2329 Gdiplus::UnitPixel,
2330 &aAttributes);
2334 return true;
2337 return false;
2340 bool WinSalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/,
2341 const Gradient& /*rGradient*/)
2343 return false;
2346 bool WinSalGraphicsImpl::TryRenderCachedNativeControl(ControlCacheKey& /*rControlCacheKey*/, int /*nX*/, int /*nY*/)
2348 return false;
2351 bool WinSalGraphicsImpl::RenderAndCacheNativeControl(OpenGLCompatibleDC& /*rWhite*/, OpenGLCompatibleDC& /*rBlack*/,
2352 int /*nX*/, int /*nY*/ , ControlCacheKey& /*aControlCacheKey*/)
2354 return false;
2357 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */