/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
// control point two
*pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
pCurrPoint++;
*pWinFlagAry++ = PT_BEZIERTO;
// end point
*pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
pCurrPoint++;
*pWinFlagAry++ = PT_BEZIERTO;
nCurrPoint += 3;
pCurrFlag += 3; continue;
}
}
// regular line point
*pWinPointAry++ = POINT { static_cast<LONG>(pCurrPoint->getX()), static_cast<LONG>(pCurrPoint->getY()) };
pCurrPoint++;
*pWinFlagAry++ = PT_LINETO;
++pCurrFlag;
++nCurrPoint;
}
// end figure
pWinFlagAry[-1] |= PT_CLOSEFIGURE;
}
}
}
void MakeInvisibleArea(const RECT& rSrcRect, int nLeft, int nTop, int nRight, int nBottom,
HRGN& rhInvalidateRgn)
{ if (!rhInvalidateRgn)
{
rhInvalidateRgn = CreateRectRgnIndirect(&rSrcRect);
}
// do we have to invalidate also the overlapping regions? if ( bWindowInvalidate && mrParent.isWindow() )
{ // compute and invalidate those parts that were either off-screen or covered by other windows // while performing the above BitBlt // those regions then have to be invalidated as they contain useless/wrong data
RECT aSrcRect;
RECT aClipRect;
RECT aTempRect;
RECT aTempRect2;
HRGN hTempRgn;
HWND hWnd;
// compute the parts that are off screen (ie invisible)
RECT theScreen;
ImplSalGetWorkArea( nullptr, &theScreen, nullptr ); // find the screen area taking multiple monitors into account
ImplCalcOutSideRgn( aSrcRect, theScreen.left, theScreen.top, theScreen.right, theScreen.bottom, hInvalidateRgn );
// calculate regions that are covered by other windows
HRGN hTempRgn2 = nullptr;
HWND hWndTopWindow = mrParent.gethWnd(); // Find the TopLevel Window, because only Windows which are in // in the foreground of our TopLevel window must be considered if ( GetWindowStyle( hWndTopWindow ) & WS_CHILD )
{
RECT aTempRect3 = aSrcRect; do
{
hWndTopWindow = ::GetParent( hWndTopWindow );
// Test if the Parent clips our window
GetClientRect( hWndTopWindow, &aTempRect );
POINT aPt2;
aPt2.x = 0;
aPt2.y = 0;
ClientToScreen( hWndTopWindow, &aPt2 );
aTempRect.left += aPt2.x;
aTempRect.top += aPt2.y;
aTempRect.right += aPt2.x;
aTempRect.bottom += aPt2.y;
IntersectRect( &aTempRect3, &aTempRect3, &aTempRect );
} while ( GetWindowStyle( hWndTopWindow ) & WS_CHILD );
// If one or more Parents clip our window, then we must // calculate the outside area if ( !EqualRect( &aSrcRect, &aTempRect3 ) )
{
ImplCalcOutSideRgn( aSrcRect,
aTempRect3.left, aTempRect3.top,
aTempRect3.right, aTempRect3.bottom,
hInvalidateRgn );
}
} // retrieve the top-most (z-order) child window
hWnd = GetWindow( GetDesktopWindow(), GW_CHILD ); while ( hWnd )
{ if ( hWnd == hWndTopWindow ) break; if ( IsWindowVisible( hWnd ) && !IsIconic( hWnd ) )
{
GetWindowRect( hWnd, &aTempRect ); if ( IntersectRect( &aTempRect2, &aSrcRect, &aTempRect ) )
{ // hWnd covers part or all of aSrcRect if ( !hInvalidateRgn )
hInvalidateRgn = CreateRectRgnIndirect( &aSrcRect );
// get full bounding box of hWnd
hTempRgn = CreateRectRgnIndirect( &aTempRect );
// get region of hWnd (the window may be shaped) if ( !hTempRgn2 )
hTempRgn2 = CreateRectRgn( 0, 0, 0, 0 ); int nRgnType = GetWindowRgn( hWnd, hTempRgn2 ); if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
{ // convert window region to screen coordinates
OffsetRgn( hTempRgn2, aTempRect.left, aTempRect.top ); // and intersect with the window's bounding box
CombineRgn( hTempRgn, hTempRgn, hTempRgn2, RGN_AND );
} // finally compute that part of aSrcRect which is not covered by any parts of hWnd
CombineRgn( hInvalidateRgn, hInvalidateRgn, hTempRgn, RGN_DIFF );
DeleteRegion( hTempRgn );
}
} // retrieve the next window in the z-order, i.e. the window below hwnd
hWnd = GetWindow( hWnd, GW_HWNDNEXT );
} if ( hTempRgn2 )
DeleteRegion( hTempRgn2 ); if ( hInvalidateRgn )
{ // hInvalidateRgn contains the fully visible parts of the original srcRect
hTempRgn = CreateRectRgnIndirect( &aSrcRect ); // subtract it from the original rect to get the occluded parts int nRgnType = CombineRgn( hInvalidateRgn, hTempRgn, hInvalidateRgn, RGN_DIFF );
DeleteRegion( hTempRgn );
if ( (nRgnType != ERROR) && (nRgnType != NULLREGION) )
{ // move the occluded parts to the destination pos int nOffX = static_cast<int>(nDestX-nSrcX); int nOffY = static_cast<int>(nDestY-nSrcY);
OffsetRgn( hInvalidateRgn, nOffX-aPt.x, nOffY-aPt.y );
// by excluding hInvalidateRgn from the system's clip region // we will prevent bitblt from copying useless data // especially now shadows from overlapping windows will appear (#i36344)
hOldClipRgn = CreateRectRgn( 0, 0, 0, 0 );
nOldClipRgnType = GetClipRgn( mrParent.getHDC(), hOldClipRgn );
bRestoreClipRgn = true; // indicate changed clipregion and force invalidate
ExtSelectClipRgn( mrParent.getHDC(), hInvalidateRgn, RGN_DIFF );
}
}
}
}
if ( bInvalidate )
{
InvalidateRgn( mrParent.gethWnd(), hInvalidateRgn, TRUE ); // here we only initiate an update if this is the MainThread, // so that there is no deadlock when handling the Paint event, // as the SolarMutex is already held by this Thread
SalData* pSalData = GetSalData();
DWORD nCurThreadId = GetCurrentThreadId(); if ( pSalData->mnAppThreadId == nCurThreadId )
UpdateWindow( mrParent.gethWnd() );
}
if(bTryDirectPaint)
{ // only paint direct when no scaling and no MapMode, else the // more expensive conversions may be done for short-time Bitmap/BitmapEx // used for buffering only if(rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight)
{
bTryDirectPaint = false;
}
}
// try to draw using GdiPlus directly if(bTryDirectPaint && TryDrawBitmapGDIPlus(rPosAry, rSalBitmap))
{ return;
}
// fall back old stuff
assert(dynamic_cast<const WinSalBitmap*>(&rSalBitmap));
// WIN/WNT seems to have a minor problem mapping the correct color of the // mask to the palette if we draw the DIB directly ==> draw DDB if( ( GetBitCount() <= 8 ) && rTransparentBitmap.ImplGethDIB() && rTransparentBitmap.GetBitCount() == 1 )
{
WinSalBitmap aTmp;
// hMemDC contains a 1x1 bitmap of the right color - stretch-blit // that to dest hdc bool bRet = GdiAlphaBlend(mrParent.getHDC(), nX, nY, nWidth, nHeight,
hMemDC.get(), 0,0,1,1,
aFunc ) == TRUE;
return bRet;
}
void WinSalGraphicsImpl::drawMask(const SalTwoRect& rPosAry, const SalBitmap& rSSalBitmap,
Color nMaskColor)
{
SAL_WARN_IF( mrParent.isPrinter(), "vcl", "No transparency print possible!" );
// WIN/WNT seems to have a minor problem mapping the correct color of the // mask to the palette if we draw the DIB directly ==> draw DDB if( ( GetBitCount() <= 8 ) && rSalBitmap.ImplGethDIB() && rSalBitmap.GetBitCount() == 1 )
{
WinSalBitmap aTmp;
// #i122149# try to avoid usage of tools::PolyPolygon ClipRegions when tools::PolyPolygon is no curve // and only contains horizontal/vertical edges. In that case, use the fallback // in GetRegionRectangles which will use vcl::Region::GetAsRegionBand() which will do // the correct polygon-to-RegionBand transformation. // Background is that when using the same Rectangle as rectangle or as Polygon // clip region will lead to different results; the polygon-based one will be // one pixel less to the right and down (see GDI docu for CreatePolygonRgn). This // again is because of the polygon-nature and it's classic handling when filling. // This also means that all cases which use a 'true' polygon-based incarnation of // a vcl::Region should know what they do - it may lead to repaint errors. if(bUsePolygon && bTryToAvoidPolygon)
{ const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() );
if(bUsePolygon)
{ // #i122149# check the comment above to know that this may lead to potential repaint // problems. It may be solved (if needed) by scaling the polygon by one in X // and Y. Currently the workaround to only use it if really unavoidable will // solve most cases. When someone is really using polygon-based Regions he // should know what he is doing. // Added code to do that scaling to check if it works, testing it. const basegfx::B2DPolyPolygon aPolyPolygon( i_rClip.GetAsB2DPolyPolygon() ); const sal_uInt32 nCount(aPolyPolygon.count());
// tdf#40863 For CustomShapes there is a hack (see // f64ef72743e55389e446e0d4bc6febd475011023) that adds polygons // with a single point in top-left and bottom-right corner // of the BoundRect to be able to determine the correct BoundRect // in the slideshow. Unfortunately, CreatePolyPolygonRgn below // fails with polygons containing a single pixel, so clipping is // lost. For now, use only polygons with more than two points - the // ones that may have an area. // Note: polygons with one point which are curves may have an area, // but the polygon is already subdivided here, so no need to test // this. if(nPoints > 2)
{
aPolyCounts[nTargetCount] = nPoints;
nTargetCount++;
for( sal_uInt32 b = 0; b < nPoints; b++ )
{
basegfx::B2DPoint aPt(aPoly.getB2DPoint(b));
if(bExpandByOneInXandY)
{
aPt = aExpand * aPt;
}
POINT aPOINT; // #i122149# do correct rounding
aPOINT.x = basegfx::fround(aPt.getX());
aPOINT.y = basegfx::fround(aPt.getY());
aPolyPoints.push_back( aPOINT );
}
}
}
// create clip region from ClipRgnData if(0 == mrParent.mpClipRgnData->rdh.nCount)
{ // #i123585# region is empty; this may happen when e.g. a tools::PolyPolygon is given // that contains no polygons or only empty ones (no width/height). This is // perfectly fine and we are done, except setting it (see end of method)
} elseif(1 == mrParent.mpClipRgnData->rdh.nCount)
{
RECT* pRect = &(mrParent.mpClipRgnData->rdh.rcBound);
mrParent.mhRegion = CreateRectRgn( pRect->left, pRect->top,
pRect->right, pRect->bottom );
} elseif(mrParent.mpClipRgnData->rdh.nCount > 1)
{
sal_uLong nSize = mrParent.mpClipRgnData->rdh.nRgnSize+sizeof(RGNDATAHEADER);
mrParent.mhRegion = ExtCreateRegion( nullptr, nSize, mrParent.mpClipRgnData );
// if ExtCreateRegion(...) is not supported if( !mrParent.mhRegion )
{
RGNDATAHEADER const & pHeader = mrParent.mpClipRgnData->rdh;
// debug code if you want to check range of the newly applied ClipRegion //RECT aBound; //const int aRegionType = GetRgnBox(mrParent.mhRegion, &aBound);
} else
{ // #i123585# See above, this is a valid case, execute it
SelectClipRgn( mrParent.getHDC(), nullptr );
}
}
// set new data
mnPenColor = nPenColor;
maLineColor = nColor;
mbPen = true;
mbStockPen = bStockPen;
}
HPEN WinSalGraphicsImpl::SearchStockPen(COLORREF nPenColor)
{ // Only screen, because printer has problems, when we use stock objects. if (mrParent.isPrinter()) return nullptr;
const SalData* pSalData = GetSalData();
for (sal_uInt16 i = 0; i < pSalData->mnStockPenCount; i++)
{ if (nPenColor == pSalData->maStockPenColorAry[i]) return pSalData->mhStockPenAry[i];
}
// set new data
mnBrushColor = nBrushColor;
maFillColor = nColor;
mbBrush = true;
mbStockBrush = bStockBrush;
}
HBRUSH WinSalGraphicsImpl::SearchStockBrush(COLORREF nBrushColor)
{ // Only screen, because printer has problems, when we use stock objects. if (!mrParent.isPrinter())
{ const SalData* pSalData = GetSalData();
for (sal_uInt16 i = 0; i < pSalData->mnStockBrushCount; i++)
{ if (nBrushColor == pSalData->maStockBrushColorAry[i]) return pSalData->mhStockBrushAry[i];
}
}
// Polyline seems to uses LineTo, which doesn't paint the last pixel (see 87eb8f8ee) if ( !mrParent.isPrinter() )
DrawPixelImpl( pWinPtAry[nPoints-1].x, pWinPtAry[nPoints-1].y, mnPenColor );
}
if ( pWinPointAry != aWinPointAry ) delete [] pWinPointAry; if ( pWinPointAryAry != aWinPointAryAry ) delete [] pWinPointAryAry;
}
bool WinSalGraphicsImpl::drawPolyLineBezier( sal_uInt32 nPoints, const Point* pPtAry, const PolyFlags* pFlgAry )
{ // #100127# draw an array of points which might also contain bezier control points if (!nPoints) returntrue;
const HDC hdc = mrParent.getHDC();
// TODO: profile whether the following options are faster: // a) look ahead and draw consecutive bezier or line segments by PolyBezierTo/PolyLineTo resp. // b) convert our flag array to window's and use PolyDraw
MoveToEx(hdc, static_cast<LONG>(pPtAry->getX()), static_cast<LONG>(pPtAry->getY()), nullptr);
++pPtAry;
++pFlgAry;
for(sal_uInt32 i = 1; i < nPoints; ++i)
{ if(*pFlgAry != PolyFlags::Control)
{
LineTo(hdc, pPtAry->getX(), pPtAry->getY());
} elseif(nPoints - i > 2)
{
POINT bezierPoints[] = {
POINT { static_cast<LONG>(pPtAry[0].getX()), static_cast<LONG>(pPtAry[0].getY()) },
POINT { static_cast<LONG>(pPtAry[1].getX()), static_cast<LONG>(pPtAry[1].getY()) },
POINT { static_cast<LONG>(pPtAry[2].getX()), static_cast<LONG>(pPtAry[2].getY()) },
};
PolyBezierTo(hdc, bezierPoints, 3);
i += 2;
pPtAry += 2;
pFlgAry += 2;
}
// tdf#99165 MS Gdiplus cannot handle creating correct extra geometry for fat lines // with LineCap or LineJoin when a bezier segment starts or ends trivial, e.g. has // no 1st or 2nd control point, despite that these are mathematically correct definitions // (basegfx can handle that). // Caution: This error (and it's correction) might be necessary for other graphical // sub-systems in a similar way. // tdf#101026 The 1st attempt to create a mathematically correct replacement control // vector was wrong. Best alternative is one as close as possible which means short. if(!b1stControlPointUsed)
{
aCa = aCurr + ((aCb - aCurr) * 0.0005);
} elseif(!b2ndControlPointUsed)
{
aCb = aNext + ((aCa - aNext) * 0.0005);
}
class SystemDependentData_GraphicsPath : public basegfx::SystemDependentData
{ private: // the path data itself
std::shared_ptr<Gdiplus::GraphicsPath> mpGraphicsPath;
// all other values the triangulation is based on and // need to be compared with to check for data validity bool mbNoLineJoin;
std::vector< double > maStroke;
// Set full (Object-to-Device) transformation - if used if(rObjectToDevice.isIdentity())
{
aGraphics.ResetTransform();
} else
{
Gdiplus::Matrix aMatrix;
// prepare local instance of Gdiplus::GraphicsPath
std::shared_ptr<Gdiplus::GraphicsPath> pGraphicsPath;
// try to access buffered data
std::shared_ptr<SystemDependentData_GraphicsPath> pSystemDependentData_GraphicsPath(
rPolyPolygon.getSystemDependentData<SystemDependentData_GraphicsPath>(basegfx::SDD_Type::SDDType_GraphicsPath));
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.