/* -*- 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 . */ #include #include #include #include #include #include #include "gdiimpl.hxx" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #undef SALGDI2_TESTTRANS #if (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS #define DBG_TESTTRANS( _def_drawable ) \ { \ XCopyArea( pXDisp, _def_drawable, aDrawable, GetCopyGC(), \ 0, 0, \ rPosAry.mnDestWidth, rPosAry.mnDestHeight, \ 0, 0 ); \ } #else // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS #define DBG_TESTTRANS( _def_drawable ) #endif // (OSL_DEBUG_LEVEL > 1) && defined SALGDI2_TESTTRANS /* From */ typedef unsigned long Pixel; class SalPolyLine { std::vector Points_; public: SalPolyLine(sal_uLong nPoints, const SalPoint *p) : Points_(nPoints+1) { for (sal_uLong i = 0; i < nPoints; ++i) { Points_[i].x = static_cast(p[i].mnX); Points_[i].y = static_cast(p[i].mnY); } Points_[nPoints] = Points_[0]; // close polyline } const XPoint &operator[](sal_uLong n) const { return Points_[n]; } XPoint &operator[](sal_uLong n) { return Points_[n]; } }; namespace { void setForeBack(XGCValues& rValues, const SalColormap& rColMap, const SalBitmap& rSalBitmap) { rValues.foreground = rColMap.GetWhitePixel(); rValues.background = rColMap.GetBlackPixel(); //fdo#33455 and fdo#80160 handle 1 bit depth pngs with palette entries //to set fore/back colors SalBitmap& rBitmap = const_cast(rSalBitmap); if (BitmapBuffer* pBitmapBuffer = rBitmap.AcquireBuffer(BitmapAccessMode::Read)) { const BitmapPalette& rPalette = pBitmapBuffer->maPalette; if (rPalette.GetEntryCount() == 2) { const BitmapColor aWhite(rPalette[rPalette.GetBestIndex(COL_WHITE)]); rValues.foreground = rColMap.GetPixel(aWhite); const BitmapColor aBlack(rPalette[rPalette.GetBestIndex(COL_BLACK)]); rValues.background = rColMap.GetPixel(aBlack); } rBitmap.ReleaseBuffer(pBitmapBuffer, BitmapAccessMode::Read); } } } X11SalGraphicsImpl::X11SalGraphicsImpl(X11SalGraphics& rParent): mrParent(rParent), mnBrushColor( 0xFF, 0xFF, 0XFF ), mpBrushGC(nullptr), mnBrushPixel(0), mbPenGC(false), mbBrushGC(false), mbCopyGC(false), mbInvertGC(false), mbInvert50GC(false), mbStippleGC(false), mbTrackingGC(false), mbDitherBrush(false), mbXORMode(false), mpPenGC(nullptr), mnPenColor( 0x00, 0x00, 0x00 ), mnPenPixel(0), mpMonoGC(nullptr), mpCopyGC(nullptr), mpMaskGC(nullptr), mpInvertGC(nullptr), mpInvert50GC(nullptr), mpStippleGC(nullptr), mpTrackingGC(nullptr) { } X11SalGraphicsImpl::~X11SalGraphicsImpl() { } void X11SalGraphicsImpl::Init() { mnPenPixel = mrParent.GetPixel( mnPenColor ); mnBrushPixel = mrParent.GetPixel( mnBrushColor ); } XID X11SalGraphicsImpl::GetXRenderPicture() { XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); if( !mrParent.m_aXRenderPicture ) { // check xrender support for matching visual XRenderPictFormat* pXRenderFormat = mrParent.GetXRenderFormat(); if( !pXRenderFormat ) return 0; // get the matching xrender target for drawable mrParent.m_aXRenderPicture = rRenderPeer.CreatePicture( mrParent.hDrawable_, pXRenderFormat, 0, nullptr ); } { // reset clip region // TODO: avoid clip reset if already done XRenderPictureAttributes aAttr; aAttr.clip_mask = None; rRenderPeer.ChangePicture( mrParent.m_aXRenderPicture, CPClipMask, &aAttr ); } return mrParent.m_aXRenderPicture; } static void freeGC(Display *pDisplay, GC& rGC) { if( rGC ) { XFreeGC( pDisplay, rGC ); rGC = None; } } void X11SalGraphicsImpl::freeResources() { Display *pDisplay = mrParent.GetXDisplay(); freeGC( pDisplay, mpPenGC ); freeGC( pDisplay, mpBrushGC ); freeGC( pDisplay, mpMonoGC ); freeGC( pDisplay, mpTrackingGC ); freeGC( pDisplay, mpCopyGC ); freeGC( pDisplay, mpMaskGC ); freeGC( pDisplay, mpInvertGC ); freeGC( pDisplay, mpInvert50GC ); freeGC( pDisplay, mpStippleGC ); mbTrackingGC = mbPenGC = mbBrushGC = mbCopyGC = mbInvertGC = mbInvert50GC = mbStippleGC = false; } GC X11SalGraphicsImpl::CreateGC( Drawable hDrawable, unsigned long nMask ) { XGCValues values; values.graphics_exposures = False; values.foreground = mrParent.m_pColormap->GetBlackPixel() ^ mrParent.m_pColormap->GetWhitePixel(); values.function = GXxor; values.line_width = 1; values.fill_style = FillStippled; values.stipple = mrParent.GetDisplay()->GetInvert50( mrParent.m_nXScreen ); values.subwindow_mode = ClipByChildren; return XCreateGC( mrParent.GetXDisplay(), hDrawable, nMask | GCSubwindowMode, &values ); } inline GC X11SalGraphicsImpl::GetCopyGC() { if( mbXORMode ) return GetInvertGC(); if( !mpCopyGC ) mpCopyGC = CreateGC( mrParent.GetDrawable() ); if( !mbCopyGC ) { mrParent.SetClipRegion( mpCopyGC ); mbCopyGC = true; } return mpCopyGC; } GC X11SalGraphicsImpl::GetTrackingGC() { const char dash_list[2] = {2, 2}; if( !mpTrackingGC ) { XGCValues values; values.graphics_exposures = False; values.foreground = mrParent.m_pColormap->GetBlackPixel() ^ mrParent.m_pColormap->GetWhitePixel(); values.function = GXxor; values.line_width = 1; values.line_style = LineOnOffDash; mpTrackingGC = XCreateGC( mrParent.GetXDisplay(), mrParent.GetDrawable(), GCGraphicsExposures | GCForeground | GCFunction | GCLineWidth | GCLineStyle, &values ); XSetDashes( mrParent.GetXDisplay(), mpTrackingGC, 0, dash_list, 2 ); } if( !mbTrackingGC ) { mrParent.SetClipRegion( mpTrackingGC ); mbTrackingGC = true; } return mpTrackingGC; } GC X11SalGraphicsImpl::GetInvertGC() { if( !mpInvertGC ) mpInvertGC = CreateGC( mrParent.GetDrawable(), GCGraphicsExposures | GCForeground | GCFunction | GCLineWidth ); if( !mbInvertGC ) { mrParent.SetClipRegion( mpInvertGC ); mbInvertGC = true; } return mpInvertGC; } GC X11SalGraphicsImpl::GetInvert50GC() { if( !mpInvert50GC ) { XGCValues values; values.graphics_exposures = False; values.foreground = mrParent.m_pColormap->GetWhitePixel(); values.background = mrParent.m_pColormap->GetBlackPixel(); values.function = GXinvert; values.line_width = 1; values.line_style = LineSolid; unsigned long const nValueMask = GCGraphicsExposures | GCForeground | GCBackground | GCFunction | GCLineWidth | GCLineStyle | GCFillStyle | GCStipple; values.fill_style = FillStippled; values.stipple = mrParent.GetDisplay()->GetInvert50( mrParent.m_nXScreen ); mpInvert50GC = XCreateGC( mrParent.GetXDisplay(), mrParent.GetDrawable(), nValueMask, &values ); } if( !mbInvert50GC ) { mrParent.SetClipRegion( mpInvert50GC ); mbInvert50GC = true; } return mpInvert50GC; } inline GC X11SalGraphicsImpl::GetStippleGC() { if( !mpStippleGC ) mpStippleGC = CreateGC( mrParent.GetDrawable(), GCGraphicsExposures | GCFillStyle | GCLineWidth ); if( !mbStippleGC ) { XSetFunction( mrParent.GetXDisplay(), mpStippleGC, mbXORMode ? GXxor : GXcopy ); mrParent.SetClipRegion( mpStippleGC ); mbStippleGC = true; } return mpStippleGC; } GC X11SalGraphicsImpl::SelectBrush() { Display *pDisplay = mrParent.GetXDisplay(); SAL_WARN_IF( mnBrushColor == SALCOLOR_NONE, "vcl", "Brush Transparent" ); if( !mpBrushGC ) { XGCValues values; values.subwindow_mode = ClipByChildren; values.fill_rule = EvenOddRule; // Pict import/ Gradient values.graphics_exposures = False; mpBrushGC = XCreateGC( pDisplay, mrParent.hDrawable_, GCSubwindowMode | GCFillRule | GCGraphicsExposures, &values ); } if( !mbBrushGC ) { if( !mbDitherBrush ) { XSetFillStyle ( pDisplay, mpBrushGC, FillSolid ); XSetForeground( pDisplay, mpBrushGC, mnBrushPixel ); } else { XSetFillStyle ( pDisplay, mpBrushGC, FillTiled ); XSetTile ( pDisplay, mpBrushGC, mrParent.hBrush_ ); } XSetFunction ( pDisplay, mpBrushGC, mbXORMode ? GXxor : GXcopy ); mrParent.SetClipRegion( mpBrushGC ); mbBrushGC = true; } return mpBrushGC; } GC X11SalGraphicsImpl::SelectPen() { Display *pDisplay = mrParent.GetXDisplay(); if( !mpPenGC ) { XGCValues values; values.subwindow_mode = ClipByChildren; values.fill_rule = EvenOddRule; // Pict import/ Gradient values.graphics_exposures = False; mpPenGC = XCreateGC( pDisplay, mrParent.hDrawable_, GCSubwindowMode | GCFillRule | GCGraphicsExposures, &values ); } if( !mbPenGC ) { if( mnPenColor != SALCOLOR_NONE ) XSetForeground( pDisplay, mpPenGC, mnPenPixel ); XSetFunction ( pDisplay, mpPenGC, mbXORMode ? GXxor : GXcopy ); mrParent.SetClipRegion( mpPenGC ); mbPenGC = true; } return mpPenGC; } void X11SalGraphicsImpl::DrawLines(sal_uInt32 nPoints, const SalPolyLine &rPoints, GC pGC, bool bClose) { // calculate how many lines XWindow can draw in one go sal_uLong nMaxLines = (mrParent.GetDisplay()->GetMaxRequestSize() - sizeof(xPolyPointReq)) / sizeof(xPoint); if( nMaxLines > nPoints ) nMaxLines = nPoints; // print all lines that XWindows can draw sal_uLong n; for( n = 0; nPoints - n > nMaxLines; n += nMaxLines - 1 ) XDrawLines( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, const_cast(&rPoints[n]), nMaxLines, CoordModeOrigin ); if( n < nPoints ) XDrawLines( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, const_cast(&rPoints[n]), nPoints - n, CoordModeOrigin ); if( bClose ) { if( rPoints[nPoints-1].x != rPoints[0].x || rPoints[nPoints-1].y != rPoints[0].y ) drawLine( rPoints[nPoints-1].x, rPoints[nPoints-1].y, rPoints[0].x, rPoints[0].y ); } } void X11SalGraphicsImpl::copyBits( const SalTwoRect& rPosAry, SalGraphics *pSSrcGraphics ) { X11SalGraphics* pSrcGraphics = pSSrcGraphics ? static_cast(pSSrcGraphics) : &mrParent; if( rPosAry.mnSrcWidth <= 0 || rPosAry.mnSrcHeight <= 0 || rPosAry.mnDestWidth <= 0 || rPosAry.mnDestHeight <= 0 ) { return; } int n; if( pSrcGraphics == &mrParent ) { n = 2; } else if( pSrcGraphics->bWindow_ ) { // window or compatible virtual device if( pSrcGraphics->GetDisplay() == mrParent.GetDisplay() && pSrcGraphics->m_nXScreen == mrParent.m_nXScreen && pSrcGraphics->GetVisual().GetDepth() == mrParent.GetVisual().GetDepth() ) n = 2; // same Display else n = 1; // printer or other display } else if( pSrcGraphics->bVirDev_ ) { n = 1; // window or compatible virtual device } else n = 0; if( n == 2 && rPosAry.mnSrcWidth == rPosAry.mnDestWidth && rPosAry.mnSrcHeight == rPosAry.mnDestHeight ) { // #i60699# Need to generate graphics exposures (to repaint // obscured areas beneath overlapping windows), src and dest // are the same window. const bool bNeedGraphicsExposures( pSrcGraphics == &mrParent && !mrParent.bVirDev_ && pSrcGraphics->bWindow_ ); GC pCopyGC = GetCopyGC(); if( bNeedGraphicsExposures ) XSetGraphicsExposures( mrParent.GetXDisplay(), pCopyGC, True ); XCopyArea( mrParent.GetXDisplay(), pSrcGraphics->GetDrawable(), // source mrParent.GetDrawable(), // destination pCopyGC, // destination clipping rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight, rPosAry.mnDestX, rPosAry.mnDestY ); if( bNeedGraphicsExposures ) { mrParent.YieldGraphicsExpose(); if( pCopyGC ) XSetGraphicsExposures( mrParent.GetXDisplay(), pCopyGC, False ); } } else if( n ) { // #i60699# No chance to handle graphics exposures - we copy // to a temp bitmap first, into which no repaints are // technically possible. std::shared_ptr xDDB(pSrcGraphics->getBitmap( rPosAry.mnSrcX, rPosAry.mnSrcY, rPosAry.mnSrcWidth, rPosAry.mnSrcHeight )); if( !xDDB ) { SAL_WARN( "vcl", "SalGraphics::CopyBits !pSrcGraphics->GetBitmap()" ); return; } SalTwoRect aPosAry( rPosAry ); aPosAry.mnSrcX = 0; aPosAry.mnSrcY = 0; drawBitmap( aPosAry, *xDDB ); } else { SAL_WARN( "vcl", "X11SalGraphicsImpl::CopyBits from Printer not yet implemented" ); } } void X11SalGraphicsImpl::copyArea ( long nDestX, long nDestY, long nSrcX, long nSrcY, long nSrcWidth, long nSrcHeight, bool /*bWindowInvalidate*/) { SalTwoRect aPosAry(nSrcX, nSrcY, nSrcWidth, nSrcHeight, nDestX, nDestY, nSrcWidth, nSrcHeight); copyBits(aPosAry, nullptr); } void X11SalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap ) { const SalDisplay* pSalDisp = mrParent.GetDisplay(); Display* pXDisp = pSalDisp->GetDisplay(); const Drawable aDrawable( mrParent.GetDrawable() ); const SalColormap& rColMap = pSalDisp->GetColormap( mrParent.m_nXScreen ); const long nDepth = mrParent.GetDisplay()->GetVisual( mrParent.m_nXScreen ).GetDepth(); GC aGC( GetCopyGC() ); XGCValues aOldVal, aNewVal; int nValues = GCForeground | GCBackground; if( rSalBitmap.GetBitCount() == 1 ) { // set foreground/background values for 1Bit bitmaps XGetGCValues( pXDisp, aGC, nValues, &aOldVal ); setForeBack(aNewVal, rColMap, rSalBitmap); XChangeGC( pXDisp, aGC, nValues, &aNewVal ); } static_cast(rSalBitmap).ImplDraw( aDrawable, mrParent.m_nXScreen, nDepth, rPosAry, aGC ); if( rSalBitmap.GetBitCount() == 1 ) XChangeGC( pXDisp, aGC, nValues, &aOldVal ); XFlush( pXDisp ); } void X11SalGraphicsImpl::drawBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSrcBitmap, const SalBitmap& rMaskBitmap ) { // decide if alpha masking or transparency masking is needed BitmapBuffer* pAlphaBuffer = const_cast(rMaskBitmap).AcquireBuffer( BitmapAccessMode::Read ); if( pAlphaBuffer != nullptr ) { ScanlineFormat nMaskFormat = pAlphaBuffer->mnFormat; const_cast(rMaskBitmap).ReleaseBuffer( pAlphaBuffer, BitmapAccessMode::Read ); if( nMaskFormat == ScanlineFormat::N8BitPal ) drawAlphaBitmap( rPosAry, rSrcBitmap, rMaskBitmap ); } drawMaskedBitmap( rPosAry, rSrcBitmap, rMaskBitmap ); } void X11SalGraphicsImpl::drawMaskedBitmap( const SalTwoRect& rPosAry, const SalBitmap& rSalBitmap, const SalBitmap& rTransBitmap ) { const SalDisplay* pSalDisp = mrParent.GetDisplay(); Display* pXDisp = pSalDisp->GetDisplay(); Drawable aDrawable( mrParent.GetDrawable() ); // figure work mode depth. If this is a VDev Drawable, use its // bitdepth to create pixmaps for, otherwise, XCopyArea will // refuse to work. const sal_uInt16 nDepth( mrParent.m_pVDev ? static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() : pSalDisp->GetVisual( mrParent.m_nXScreen ).GetDepth() ); Pixmap aFG( limitXCreatePixmap( pXDisp, aDrawable, rPosAry.mnDestWidth, rPosAry.mnDestHeight, nDepth ) ); Pixmap aBG( limitXCreatePixmap( pXDisp, aDrawable, rPosAry.mnDestWidth, rPosAry.mnDestHeight, nDepth ) ); if( aFG && aBG ) { GC aTmpGC; XGCValues aValues; setForeBack(aValues, pSalDisp->GetColormap(mrParent.m_nXScreen), rSalBitmap); const int nValues = GCFunction | GCForeground | GCBackground; SalTwoRect aTmpRect( rPosAry ); aTmpRect.mnDestX = aTmpRect.mnDestY = 0; // draw paint bitmap in pixmap #1 aValues.function = GXcopy; aTmpGC = XCreateGC( pXDisp, aFG, nValues, &aValues ); static_cast(rSalBitmap).ImplDraw( aFG, mrParent.m_nXScreen, nDepth, aTmpRect, aTmpGC ); DBG_TESTTRANS( aFG ); // draw background in pixmap #2 XCopyArea( pXDisp, aDrawable, aBG, aTmpGC, rPosAry.mnDestX, rPosAry.mnDestY, rPosAry.mnDestWidth, rPosAry.mnDestHeight, 0, 0 ); DBG_TESTTRANS( aBG ); // mask out paint bitmap in pixmap #1 (transparent areas 0) aValues.function = GXand; aValues.foreground = 0x00000000; aValues.background = 0xffffffff; XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); static_cast(rTransBitmap).ImplDraw( aFG, mrParent.m_nXScreen, 1, aTmpRect, aTmpGC ); DBG_TESTTRANS( aFG ); // #105055# For XOR mode, keep background behind bitmap intact if( !mbXORMode ) { // mask out background in pixmap #2 (nontransparent areas 0) aValues.function = GXand; aValues.foreground = 0xffffffff; aValues.background = 0x00000000; XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); static_cast(rTransBitmap).ImplDraw( aBG, mrParent.m_nXScreen, 1, aTmpRect, aTmpGC ); DBG_TESTTRANS( aBG ); } // merge pixmap #1 and pixmap #2 in pixmap #2 aValues.function = GXxor; aValues.foreground = 0xffffffff; aValues.background = 0x00000000; XChangeGC( pXDisp, aTmpGC, nValues, &aValues ); XCopyArea( pXDisp, aFG, aBG, aTmpGC, 0, 0, rPosAry.mnDestWidth, rPosAry.mnDestHeight, 0, 0 ); DBG_TESTTRANS( aBG ); // #105055# Disable XOR temporarily bool bOldXORMode( mbXORMode ); mbXORMode = false; // copy pixmap #2 (result) to background XCopyArea( pXDisp, aBG, aDrawable, GetCopyGC(), 0, 0, rPosAry.mnDestWidth, rPosAry.mnDestHeight, rPosAry.mnDestX, rPosAry.mnDestY ); DBG_TESTTRANS( aBG ); mbXORMode = bOldXORMode; XFreeGC( pXDisp, aTmpGC ); XFlush( pXDisp ); } else drawBitmap( rPosAry, rSalBitmap ); if( aFG ) XFreePixmap( pXDisp, aFG ); if( aBG ) XFreePixmap( pXDisp, aBG ); } bool X11SalGraphicsImpl::blendBitmap( const SalTwoRect&, const SalBitmap& ) { return false; } bool X11SalGraphicsImpl::blendAlphaBitmap( const SalTwoRect&, const SalBitmap&, const SalBitmap&, const SalBitmap& ) { return false; } bool X11SalGraphicsImpl::drawAlphaBitmap( const SalTwoRect& rTR, const SalBitmap& rSrcBitmap, const SalBitmap& rAlphaBmp ) { // non 8-bit alpha not implemented yet if( rAlphaBmp.GetBitCount() != 8 ) return false; // horizontal mirroring not implemented yet if( rTR.mnDestWidth < 0 ) return false; // stretched conversion is not implemented yet if( rTR.mnDestWidth != rTR.mnSrcWidth ) return false; if( rTR.mnDestHeight!= rTR.mnSrcHeight ) return false; // create destination picture Picture aDstPic = GetXRenderPicture(); if( !aDstPic ) return false; const SalDisplay* pSalDisp = mrParent.GetDisplay(); const SalVisual& rSalVis = pSalDisp->GetVisual( mrParent.m_nXScreen ); Display* pXDisplay = pSalDisp->GetDisplay(); // create source Picture int nDepth = mrParent.m_pVDev ? static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() : rSalVis.GetDepth(); const X11SalBitmap& rSrcX11Bmp = static_cast( rSrcBitmap ); ImplSalDDB* pSrcDDB = rSrcX11Bmp.ImplGetDDB( mrParent.hDrawable_, mrParent.m_nXScreen, nDepth, rTR ); if( !pSrcDDB ) return false; //#i75249# workaround for ImplGetDDB() giving us back a different depth than // we requested. E.g. mask pixmaps are always compatible with the drawable // TODO: find an appropriate picture format for these cases // then remove the workaround below and the one for #i75531# if( nDepth != pSrcDDB->ImplGetDepth() ) return false; Pixmap aSrcPM = pSrcDDB->ImplGetPixmap(); if( !aSrcPM ) return false; // create source picture // TODO: use scoped picture Visual* pSrcXVisual = rSalVis.GetVisual(); XRenderPeer& rPeer = XRenderPeer::GetInstance(); XRenderPictFormat* pSrcVisFmt = rPeer.FindVisualFormat( pSrcXVisual ); if( !pSrcVisFmt ) return false; Picture aSrcPic = rPeer.CreatePicture( aSrcPM, pSrcVisFmt, 0, nullptr ); if( !aSrcPic ) return false; // create alpha Picture // TODO: use SalX11Bitmap functionality and caching for the Alpha Pixmap // problem is that they don't provide an 8bit Pixmap on a non-8bit display BitmapBuffer* pAlphaBuffer = const_cast(rAlphaBmp).AcquireBuffer( BitmapAccessMode::Read ); // an XImage needs its data top_down // TODO: avoid wrongly oriented images in upper layers! const int nImageSize = pAlphaBuffer->mnHeight * pAlphaBuffer->mnScanlineSize; const char* pSrcBits = reinterpret_cast(pAlphaBuffer->mpBits); char* pAlphaBits = new char[ nImageSize ]; if( pAlphaBuffer->mnFormat & ScanlineFormat::TopDown ) memcpy( pAlphaBits, pSrcBits, nImageSize ); else { char* pDstBits = pAlphaBits + nImageSize; const int nLineSize = pAlphaBuffer->mnScanlineSize; for(; (pDstBits -= nLineSize) >= pAlphaBits; pSrcBits += nLineSize ) memcpy( pDstBits, pSrcBits, nLineSize ); } // the alpha values need to be inverted for XRender // TODO: make upper layers use standard alpha long* pLDst = reinterpret_cast(pAlphaBits); for( int i = nImageSize/sizeof(long); --i >= 0; ++pLDst ) *pLDst = ~*pLDst; char* pCDst = reinterpret_cast(pLDst); for( int i = nImageSize & (sizeof(long)-1); --i >= 0; ++pCDst ) *pCDst = ~*pCDst; const XRenderPictFormat* pAlphaFormat = rPeer.GetStandardFormatA8(); XImage* pAlphaImg = XCreateImage( pXDisplay, pSrcXVisual, 8, ZPixmap, 0, pAlphaBits, pAlphaBuffer->mnWidth, pAlphaBuffer->mnHeight, pAlphaFormat->depth, pAlphaBuffer->mnScanlineSize ); Pixmap aAlphaPM = limitXCreatePixmap( pXDisplay, mrParent.hDrawable_, rTR.mnDestWidth, rTR.mnDestHeight, 8 ); XGCValues aAlphaGCV; aAlphaGCV.function = GXcopy; GC aAlphaGC = XCreateGC( pXDisplay, aAlphaPM, GCFunction, &aAlphaGCV ); XPutImage( pXDisplay, aAlphaPM, aAlphaGC, pAlphaImg, rTR.mnSrcX, rTR.mnSrcY, 0, 0, rTR.mnDestWidth, rTR.mnDestHeight ); XFreeGC( pXDisplay, aAlphaGC ); XFree( pAlphaImg ); if( pAlphaBits != reinterpret_cast(pAlphaBuffer->mpBits) ) delete[] pAlphaBits; const_cast(rAlphaBmp).ReleaseBuffer( pAlphaBuffer, BitmapAccessMode::Read ); XRenderPictureAttributes aAttr; aAttr.repeat = int(true); Picture aAlphaPic = rPeer.CreatePicture( aAlphaPM, pAlphaFormat, CPRepeat, &aAttr ); if( !aAlphaPic ) return false; // set clipping if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) ) rPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion ); // paint source * mask over destination picture rPeer.CompositePicture( PictOpOver, aSrcPic, aAlphaPic, aDstPic, rTR.mnSrcX, rTR.mnSrcY, rTR.mnDestX, rTR.mnDestY, rTR.mnDestWidth, rTR.mnDestHeight ); rPeer.FreePicture( aAlphaPic ); XFreePixmap(pXDisplay, aAlphaPM); rPeer.FreePicture( aSrcPic ); return true; } bool X11SalGraphicsImpl::drawTransformedBitmap( const basegfx::B2DPoint&, const basegfx::B2DPoint&, const basegfx::B2DPoint&, const SalBitmap&, const SalBitmap*) { // here direct support for transformed bitmaps can be implemented return false; } bool X11SalGraphicsImpl::drawAlphaRect( long nX, long nY, long nWidth, long nHeight, sal_uInt8 nTransparency ) { if( ! mrParent.m_pFrame && ! mrParent.m_pVDev ) return false; if( mbPenGC || !mbBrushGC || mbXORMode ) return false; // can only perform solid fills without XOR. if( mrParent.m_pVDev && static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetDepth() < 8 ) return false; Picture aDstPic = GetXRenderPicture(); if( !aDstPic ) return false; const double fTransparency = (100 - nTransparency) * (1.0/100); const XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency); XRenderPeer& rPeer = XRenderPeer::GetInstance(); rPeer.FillRectangle( PictOpOver, aDstPic, &aRenderColor, nX, nY, nWidth, nHeight ); return true; } void X11SalGraphicsImpl::drawMask( const SalTwoRect& rPosAry, const SalBitmap &rSalBitmap, Color nMaskColor ) { const SalDisplay* pSalDisp = mrParent.GetDisplay(); Display* pXDisp = pSalDisp->GetDisplay(); Drawable aDrawable( mrParent.GetDrawable() ); Pixmap aStipple( limitXCreatePixmap( pXDisp, aDrawable, rPosAry.mnDestWidth, rPosAry.mnDestHeight, 1 ) ); if( aStipple ) { SalTwoRect aTwoRect( rPosAry ); aTwoRect.mnDestX = aTwoRect.mnDestY = 0; GC aTmpGC; XGCValues aValues; // create a stipple bitmap first (set bits are changed to unset bits and vice versa) aValues.function = GXcopyInverted; aValues.foreground = 1; aValues.background = 0; aTmpGC = XCreateGC( pXDisp, aStipple, GCFunction | GCForeground | GCBackground, &aValues ); static_cast(rSalBitmap).ImplDraw( aStipple, mrParent.m_nXScreen, 1, aTwoRect, aTmpGC ); XFreeGC( pXDisp, aTmpGC ); // Set stipple and draw rectangle GC aStippleGC( GetStippleGC() ); int nX = rPosAry.mnDestX, nY = rPosAry.mnDestY; XSetStipple( pXDisp, aStippleGC, aStipple ); XSetTSOrigin( pXDisp, aStippleGC, nX, nY ); XSetForeground( pXDisp, aStippleGC, mrParent.GetPixel( nMaskColor ) ); XFillRectangle( pXDisp, aDrawable, aStippleGC, nX, nY, rPosAry.mnDestWidth, rPosAry.mnDestHeight ); XFreePixmap( pXDisp, aStipple ); XFlush( pXDisp ); } else drawBitmap( rPosAry, rSalBitmap ); } void X11SalGraphicsImpl::ResetClipRegion() { if( mrParent.mpClipRegion ) { mbPenGC = false; mbBrushGC = false; mbCopyGC = false; mbInvertGC = false; mbInvert50GC = false; mbStippleGC = false; mbTrackingGC = false; XDestroyRegion( mrParent.mpClipRegion ); mrParent.mpClipRegion = nullptr; } } bool X11SalGraphicsImpl::setClipRegion( const vcl::Region& i_rClip ) { if( mrParent.mpClipRegion ) XDestroyRegion( mrParent.mpClipRegion ); mrParent.mpClipRegion = XCreateRegion(); RectangleVector aRectangles; i_rClip.GetRegionRectangles(aRectangles); for (auto const& rectangle : aRectangles) { const long nW(rectangle.GetWidth()); if(nW) { const long nH(rectangle.GetHeight()); if(nH) { XRectangle aRect; aRect.x = static_cast(rectangle.Left()); aRect.y = static_cast(rectangle.Top()); aRect.width = static_cast(nW); aRect.height = static_cast(nH); XUnionRectWithRegion(&aRect, mrParent.mpClipRegion, mrParent.mpClipRegion); } } } //ImplRegionInfo aInfo; //long nX, nY, nW, nH; //bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH ); //while( bRegionRect ) //{ // if ( nW && nH ) // { // XRectangle aRect; // aRect.x = (short)nX; // aRect.y = (short)nY; // aRect.width = (unsigned short)nW; // aRect.height = (unsigned short)nH; // XUnionRectWithRegion( &aRect, mrParent.mpClipRegion, mrParent.mpClipRegion ); // } // bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH ); //} // done, invalidate GCs mbPenGC = false; mbBrushGC = false; mbCopyGC = false; mbInvertGC = false; mbInvert50GC = false; mbStippleGC = false; mbTrackingGC = false; if( XEmptyRegion( mrParent.mpClipRegion ) ) { XDestroyRegion( mrParent.mpClipRegion ); mrParent.mpClipRegion= nullptr; } return true; } void X11SalGraphicsImpl::SetLineColor() { if( mnPenColor != SALCOLOR_NONE ) { mnPenColor = SALCOLOR_NONE; mbPenGC = false; } } void X11SalGraphicsImpl::SetLineColor( Color nColor ) { if( mnPenColor != nColor ) { mnPenColor = nColor; mnPenPixel = mrParent.GetPixel( nColor ); mbPenGC = false; } } void X11SalGraphicsImpl::SetFillColor() { if( mnBrushColor != SALCOLOR_NONE ) { mbDitherBrush = false; mnBrushColor = SALCOLOR_NONE; mbBrushGC = false; } } void X11SalGraphicsImpl::SetFillColor( Color nColor ) { if( mnBrushColor != nColor ) { mbDitherBrush = false; mnBrushColor = nColor; mnBrushPixel = mrParent.GetPixel( nColor ); if( TrueColor != mrParent.GetColormap().GetVisual().GetClass() && mrParent.GetColormap().GetColor( mnBrushPixel ) != mnBrushColor && nColor != Color( 0x00, 0x00, 0x00 ) // black && nColor != Color( 0x00, 0x00, 0x80 ) // blue && nColor != Color( 0x00, 0x80, 0x00 ) // green && nColor != Color( 0x00, 0x80, 0x80 ) // cyan && nColor != Color( 0x80, 0x00, 0x00 ) // red && nColor != Color( 0x80, 0x00, 0x80 ) // magenta && nColor != Color( 0x80, 0x80, 0x00 ) // brown && nColor != Color( 0x80, 0x80, 0x80 ) // gray && nColor != Color( 0xC0, 0xC0, 0xC0 ) // light gray && nColor != Color( 0x00, 0x00, 0xFF ) // light blue && nColor != Color( 0x00, 0xFF, 0x00 ) // light green && nColor != Color( 0x00, 0xFF, 0xFF ) // light cyan && nColor != Color( 0xFF, 0x00, 0x00 ) // light red && nColor != Color( 0xFF, 0x00, 0xFF ) // light magenta && nColor != Color( 0xFF, 0xFF, 0x00 ) // light brown && nColor != Color( 0xFF, 0xFF, 0xFF ) ) mbDitherBrush = mrParent.GetDitherPixmap(nColor); mbBrushGC = false; } } void X11SalGraphicsImpl::SetROPLineColor( SalROPColor nROPColor ) { switch( nROPColor ) { case SalROPColor::N0 : // 0 mnPenPixel = Pixel(0); break; case SalROPColor::N1 : // 1 mnPenPixel = static_cast(1 << mrParent.GetVisual().GetDepth()) - 1; break; case SalROPColor::Invert : // 2 mnPenPixel = static_cast(1 << mrParent.GetVisual().GetDepth()) - 1; break; } mnPenColor = mrParent.GetColormap().GetColor( mnPenPixel ); mbPenGC = false; } void X11SalGraphicsImpl::SetROPFillColor( SalROPColor nROPColor ) { switch( nROPColor ) { case SalROPColor::N0 : // 0 mnBrushPixel = Pixel(0); break; case SalROPColor::N1 : // 1 mnBrushPixel = static_cast(1 << mrParent.GetVisual().GetDepth()) - 1; break; case SalROPColor::Invert : // 2 mnBrushPixel = static_cast(1 << mrParent.GetVisual().GetDepth()) - 1; break; } mbDitherBrush = false; mnBrushColor = mrParent.GetColormap().GetColor( mnBrushPixel ); mbBrushGC = false; } void X11SalGraphicsImpl::SetXORMode( bool bSet, bool ) { if (mbXORMode != bSet) { mbXORMode = bSet; mbPenGC = false; mbBrushGC = false; mbCopyGC = false; mbInvertGC = false; mbInvert50GC = false; mbStippleGC = false; mbTrackingGC = false; } } void X11SalGraphicsImpl::drawPixel( long nX, long nY ) { if( mnPenColor != SALCOLOR_NONE ) XDrawPoint( mrParent.GetXDisplay(), mrParent.GetDrawable(), SelectPen(), nX, nY ); } void X11SalGraphicsImpl::drawPixel( long nX, long nY, Color nColor ) { if( nColor != SALCOLOR_NONE ) { Display *pDisplay = mrParent.GetXDisplay(); if( (mnPenColor == SALCOLOR_NONE) && !mbPenGC ) { SetLineColor( nColor ); XDrawPoint( pDisplay, mrParent.GetDrawable(), SelectPen(), nX, nY ); mnPenColor = SALCOLOR_NONE; mbPenGC = False; } else { GC pGC = SelectPen(); if( nColor != mnPenColor ) XSetForeground( pDisplay, pGC, mrParent.GetPixel( nColor ) ); XDrawPoint( pDisplay, mrParent.GetDrawable(), pGC, nX, nY ); if( nColor != mnPenColor ) XSetForeground( pDisplay, pGC, mnPenPixel ); } } } void X11SalGraphicsImpl::drawLine( long nX1, long nY1, long nX2, long nY2 ) { if( mnPenColor != SALCOLOR_NONE ) { XDrawLine( mrParent.GetXDisplay(), mrParent.GetDrawable(),SelectPen(), nX1, nY1, nX2, nY2 ); } } void X11SalGraphicsImpl::drawRect( long nX, long nY, long nDX, long nDY ) { if( mnBrushColor != SALCOLOR_NONE ) { XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), SelectBrush(), nX, nY, nDX, nDY ); } // description DrawRect is wrong; thus -1 if( mnPenColor != SALCOLOR_NONE ) XDrawRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), SelectPen(), nX, nY, nDX-1, nDY-1 ); } void X11SalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint *pPtAry ) { drawPolyLine( nPoints, pPtAry, false ); } void X11SalGraphicsImpl::drawPolyLine( sal_uInt32 nPoints, const SalPoint *pPtAry, bool bClose ) { if( mnPenColor != SALCOLOR_NONE ) { SalPolyLine Points( nPoints, pPtAry ); DrawLines( nPoints, Points, SelectPen(), bClose ); } } void X11SalGraphicsImpl::drawPolygon( sal_uInt32 nPoints, const SalPoint* pPtAry ) { if( nPoints == 0 ) return; if( nPoints < 3 ) { if( !mbXORMode ) { if( 1 == nPoints ) drawPixel( pPtAry[0].mnX, pPtAry[0].mnY ); else drawLine( pPtAry[0].mnX, pPtAry[0].mnY, pPtAry[1].mnX, pPtAry[1].mnY ); } return; } SalPolyLine Points( nPoints, pPtAry ); nPoints++; /* WORKAROUND: some Xservers (Xorg, VIA chipset in this case) * do not draw the visible part of a polygon * if it overlaps to the left of screen 0,y. * This happens to be the case in the gradient drawn in the * menubar background. workaround for the special case of * of a rectangle overlapping to the left. */ if (nPoints == 5 && Points[ 0 ].x == Points[ 1 ].x && Points[ 1 ].y == Points[ 2 ].y && Points[ 2 ].x == Points[ 3 ].x && Points[ 0 ].x == Points[ 4 ].x && Points[ 0 ].y == Points[ 4 ].y ) { bool bLeft = false; bool bRight = false; for(unsigned int i = 0; i < nPoints; i++ ) { if( Points[i].x < 0 ) bLeft = true; else bRight= true; } if( bLeft && ! bRight ) return; if( bLeft && bRight ) { for( unsigned int i = 0; i < nPoints; i++ ) if( Points[i].x < 0 ) Points[i].x = 0; } } if( mnBrushColor != SALCOLOR_NONE ) XFillPolygon( mrParent.GetXDisplay(), mrParent.GetDrawable(), SelectBrush(), &Points[0], nPoints, Complex, CoordModeOrigin ); if( mnPenColor != SALCOLOR_NONE ) DrawLines( nPoints, Points, SelectPen(), true ); } void X11SalGraphicsImpl::drawPolyPolygon( sal_uInt32 nPoly, const sal_uInt32 *pPoints, PCONSTSALPOINT *pPtAry ) { if( mnBrushColor != SALCOLOR_NONE ) { sal_uInt32 i, n; Region pXRegA = nullptr; for( i = 0; i < nPoly; i++ ) { n = pPoints[i]; SalPolyLine Points( n, pPtAry[i] ); if( n > 2 ) { Region pXRegB = XPolygonRegion( &Points[0], n+1, WindingRule ); if( !pXRegA ) pXRegA = pXRegB; else { XXorRegion( pXRegA, pXRegB, pXRegA ); XDestroyRegion( pXRegB ); } } } if( pXRegA ) { XRectangle aXRect; XClipBox( pXRegA, &aXRect ); GC pGC = SelectBrush(); mrParent.SetClipRegion( pGC, pXRegA ); // ??? twice XDestroyRegion( pXRegA ); mbBrushGC = false; XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, aXRect.x, aXRect.y, aXRect.width, aXRect.height ); } } if( mnPenColor != SALCOLOR_NONE ) for( sal_uInt32 i = 0; i < nPoly; i++ ) drawPolyLine( pPoints[i], pPtAry[i], true ); } bool X11SalGraphicsImpl::drawPolyLineBezier( sal_uInt32, const SalPoint*, const PolyFlags* ) { return false; } bool X11SalGraphicsImpl::drawPolygonBezier( sal_uInt32, const SalPoint*, const PolyFlags* ) { return false; } bool X11SalGraphicsImpl::drawPolyPolygonBezier( sal_uInt32, const sal_uInt32*, const SalPoint* const*, const PolyFlags* const* ) { return false; } void X11SalGraphicsImpl::invert( long nX, long nY, long nDX, long nDY, SalInvert nFlags ) { GC pGC; if( SalInvert::N50 & nFlags ) { pGC = GetInvert50GC(); XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY ); } else { if ( SalInvert::TrackFrame & nFlags ) { pGC = GetTrackingGC(); XDrawRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY ); } else { pGC = GetInvertGC(); XFillRectangle( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, nX, nY, nDX, nDY ); } } } void X11SalGraphicsImpl::invert( sal_uInt32 nPoints, const SalPoint* pPtAry, SalInvert nFlags ) { SalPolyLine Points ( nPoints, pPtAry ); GC pGC; if( SalInvert::N50 & nFlags ) pGC = GetInvert50GC(); else if ( SalInvert::TrackFrame & nFlags ) pGC = GetTrackingGC(); else pGC = GetInvertGC(); if( SalInvert::TrackFrame & nFlags ) DrawLines ( nPoints, Points, pGC, true ); else XFillPolygon( mrParent.GetXDisplay(), mrParent.GetDrawable(), pGC, &Points[0], nPoints, Complex, CoordModeOrigin ); } bool X11SalGraphicsImpl::drawEPS( long,long,long,long,void*,sal_uInt32 ) { return false; } // draw a poly-polygon bool X11SalGraphicsImpl::drawPolyPolygon( const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::B2DPolyPolygon& rPolyPolygon, double fTransparency) { // nothing to do for empty polypolygons const int nOrigPolyCount = rPolyPolygon.count(); if( nOrigPolyCount <= 0 ) return true; // nothing to do if everything is transparent if( (mnBrushColor == SALCOLOR_NONE) && (mnPenColor == SALCOLOR_NONE) ) return true; // cannot handle pencolor!=brushcolor yet if( (mnPenColor != SALCOLOR_NONE) && (mnPenColor != mnBrushColor) ) return false; // TODO: remove the env-variable when no longer needed static const char* pRenderEnv = getenv( "SAL_DISABLE_RENDER_POLY" ); if( pRenderEnv ) return false; // Fallback: Transform to DeviceCoordinates basegfx::B2DPolyPolygon aPolyPolygon(rPolyPolygon); aPolyPolygon.transform(rObjectToDevice); // snap to raster if requested const bool bSnapToRaster = !mrParent.getAntiAliasB2DDraw(); if( bSnapToRaster ) aPolyPolygon = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges( aPolyPolygon ); // don't bother with polygons outside of visible area const basegfx::B2DRange aViewRange( 0, 0, GetGraphicsWidth(), GetGraphicsHeight() ); aPolyPolygon = basegfx::utils::clipPolyPolygonOnRange( aPolyPolygon, aViewRange, true, false ); if( !aPolyPolygon.count() ) return true; // tessellate the polypolygon into trapezoids basegfx::B2DTrapezoidVector aB2DTrapVector; basegfx::utils::trapezoidSubdivide( aB2DTrapVector, aPolyPolygon ); const int nTrapCount = aB2DTrapVector.size(); if( !nTrapCount ) return true; const bool bDrawn = drawFilledTrapezoids( aB2DTrapVector.data(), nTrapCount, fTransparency ); return bDrawn; } long X11SalGraphicsImpl::GetGraphicsHeight() const { if( mrParent.m_pFrame ) return mrParent.m_pFrame->maGeometry.nHeight; else if( mrParent.m_pVDev ) return static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetHeight(); else return 0; } bool X11SalGraphicsImpl::drawFilledTrapezoids( const basegfx::B2DTrapezoid* pB2DTraps, int nTrapCount, double fTransparency ) { if( nTrapCount <= 0 ) return true; Picture aDstPic = GetXRenderPicture(); // check xrender support for this drawable if( !aDstPic ) return false; // convert the B2DTrapezoids into XRender-Trapezoids std::vector aTrapVector( nTrapCount ); const basegfx::B2DTrapezoid* pB2DTrap = pB2DTraps; for( int i = 0; i < nTrapCount; ++pB2DTrap, ++i ) { XTrapezoid& rTrap = aTrapVector[ i ] ; // set y-coordinates const double fY1 = pB2DTrap->getTopY(); rTrap.left.p1.y = rTrap.right.p1.y = rTrap.top = XDoubleToFixed( fY1 ); const double fY2 = pB2DTrap->getBottomY(); rTrap.left.p2.y = rTrap.right.p2.y = rTrap.bottom = XDoubleToFixed( fY2 ); // set x-coordinates const double fXL1 = pB2DTrap->getTopXLeft(); rTrap.left.p1.x = XDoubleToFixed( fXL1 ); const double fXR1 = pB2DTrap->getTopXRight(); rTrap.right.p1.x = XDoubleToFixed( fXR1 ); const double fXL2 = pB2DTrap->getBottomXLeft(); rTrap.left.p2.x = XDoubleToFixed( fXL2 ); const double fXR2 = pB2DTrap->getBottomXRight(); rTrap.right.p2.x = XDoubleToFixed( fXR2 ); } // get xrender Picture for polygon foreground // TODO: cache it like the target picture which uses GetXRenderPicture() XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); SalDisplay::RenderEntry& rEntry = mrParent.GetDisplay()->GetRenderEntries( mrParent.m_nXScreen )[ 32 ]; if( !rEntry.m_aPicture ) { Display* pXDisplay = mrParent.GetXDisplay(); rEntry.m_aPixmap = limitXCreatePixmap( pXDisplay, mrParent.hDrawable_, 1, 1, 32 ); XRenderPictureAttributes aAttr; aAttr.repeat = int(true); XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 ); rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr ); } // set polygon foreground color and opacity XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency ); rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); // set clipping // TODO: move into GetXRenderPicture? if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) ) rRenderPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion ); // render the trapezoids const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8(); rRenderPeer.CompositeTrapezoids( PictOpOver, rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, aTrapVector.data(), aTrapVector.size() ); return true; } bool X11SalGraphicsImpl::drawFilledTriangles( const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::triangulator::B2DTriangleVector& rTriangles, double fTransparency) { if(rTriangles.empty()) return true; Picture aDstPic = GetXRenderPicture(); // check xrender support for this drawable if( !aDstPic ) { return false; } // prepare transformation for ObjectToDevice coordinate system basegfx::B2DHomMatrix aObjectToDevice = basegfx::utils::createTranslateB2DHomMatrix(0.5, 0.5) * rObjectToDevice; // convert the Triangles into XRender-Triangles std::vector aTriVector(rTriangles.size()); sal_uInt32 nIndex(0); for(const auto& rCandidate : rTriangles) { const basegfx::B2DPoint aP1(aObjectToDevice * rCandidate.getA()); const basegfx::B2DPoint aP2(aObjectToDevice * rCandidate.getB()); const basegfx::B2DPoint aP3(aObjectToDevice * rCandidate.getC()); XTriangle& rTri(aTriVector[nIndex++]); rTri.p1.x = XDoubleToFixed(aP1.getX()); rTri.p1.y = XDoubleToFixed(aP1.getY()); rTri.p2.x = XDoubleToFixed(aP2.getX()); rTri.p2.y = XDoubleToFixed(aP2.getY()); rTri.p3.x = XDoubleToFixed(aP3.getX()); rTri.p3.y = XDoubleToFixed(aP3.getY()); } // get xrender Picture for polygon foreground // TODO: cache it like the target picture which uses GetXRenderPicture() XRenderPeer& rRenderPeer = XRenderPeer::GetInstance(); SalDisplay::RenderEntry& rEntry = mrParent.GetDisplay()->GetRenderEntries( mrParent.m_nXScreen )[ 32 ]; if( !rEntry.m_aPicture ) { Display* pXDisplay = mrParent.GetXDisplay(); rEntry.m_aPixmap = limitXCreatePixmap( pXDisplay, mrParent.hDrawable_, 1, 1, 32 ); XRenderPictureAttributes aAttr; aAttr.repeat = int(true); XRenderPictFormat* pXRPF = rRenderPeer.FindStandardFormat( PictStandardARGB32 ); rEntry.m_aPicture = rRenderPeer.CreatePicture( rEntry.m_aPixmap, pXRPF, CPRepeat, &aAttr ); } // set polygon foreground color and opacity XRenderColor aRenderColor = GetXRenderColor( mnBrushColor , fTransparency ); rRenderPeer.FillRectangle( PictOpSrc, rEntry.m_aPicture, &aRenderColor, 0, 0, 1, 1 ); // set clipping // TODO: move into GetXRenderPicture? if( mrParent.mpClipRegion && !XEmptyRegion( mrParent.mpClipRegion ) ) rRenderPeer.SetPictureClipRegion( aDstPic, mrParent.mpClipRegion ); // render the trapezoids const XRenderPictFormat* pMaskFormat = rRenderPeer.GetStandardFormatA8(); rRenderPeer.CompositeTriangles( PictOpOver, rEntry.m_aPicture, aDstPic, pMaskFormat, 0, 0, aTriVector.data(), aTriVector.size() ); return true; } namespace { class SystemDependentData_Triangulation : public basegfx::SystemDependentData { private: // the triangulation itself basegfx::triangulator::B2DTriangleVector maTriangles; // all other values the triangulation is based on and // need to be compared with to check for data validity double mfLineWidth; basegfx::B2DLineJoin meJoin; css::drawing::LineCap meCap; double mfMiterMinimumAngle; std::vector< double > maStroke; public: SystemDependentData_Triangulation( basegfx::SystemDependentDataManager& rSystemDependentDataManager, const basegfx::triangulator::B2DTriangleVector& rTriangles, double fLineWidth, basegfx::B2DLineJoin eJoin, css::drawing::LineCap eCap, double fMiterMinimumAngle, const std::vector< double >* pStroke); // MM01 // read access const basegfx::triangulator::B2DTriangleVector& getTriangles() const { return maTriangles; } double getLineWidth() const { return mfLineWidth; } const basegfx::B2DLineJoin& getJoin() const { return meJoin; } const css::drawing::LineCap& getCap() const { return meCap; } double getMiterMinimumAngle() const { return mfMiterMinimumAngle; } const std::vector< double >& getStroke() const { return maStroke; } virtual sal_Int64 estimateUsageInBytes() const override; }; } SystemDependentData_Triangulation::SystemDependentData_Triangulation( basegfx::SystemDependentDataManager& rSystemDependentDataManager, const basegfx::triangulator::B2DTriangleVector& rTriangles, double fLineWidth, basegfx::B2DLineJoin eJoin, css::drawing::LineCap eCap, double fMiterMinimumAngle, const std::vector< double >* pStroke) : basegfx::SystemDependentData(rSystemDependentDataManager), maTriangles(rTriangles), mfLineWidth(fLineWidth), meJoin(eJoin), meCap(eCap), mfMiterMinimumAngle(fMiterMinimumAngle), maStroke() { if(nullptr != pStroke) { maStroke = *pStroke; } } sal_Int64 SystemDependentData_Triangulation::estimateUsageInBytes() const { sal_Int64 nRetval(0); if(!maTriangles.empty()) { nRetval = maTriangles.size() * sizeof(basegfx::triangulator::B2DTriangle); } return nRetval; } bool X11SalGraphicsImpl::drawPolyLine( const basegfx::B2DHomMatrix& rObjectToDevice, const basegfx::B2DPolygon& rPolygon, double fTransparency, double fLineWidth, const std::vector< double >* pStroke, // MM01 basegfx::B2DLineJoin eLineJoin, css::drawing::LineCap eLineCap, double fMiterMinimumAngle, bool bPixelSnapHairline) { // short circuit if there is nothing to do if(0 == rPolygon.count() || fTransparency < 0.0 || fTransparency >= 1.0) { return true; } // need to check/handle LineWidth when ObjectToDevice transformation is used const bool bObjectToDeviceIsIdentity(rObjectToDevice.isIdentity()); basegfx::B2DHomMatrix aObjectToDeviceInv; // tdf#124848 calculate-back logical LineWidth for a hairline. // This implementation does not hand over the transformation to // the graphic sub-system, but the triangulation data is prepared // view-independent based on the logic LineWidth, so we need to // know it if(fLineWidth == 0) { fLineWidth = 1.0; if(!bObjectToDeviceIsIdentity) { if(aObjectToDeviceInv.isIdentity()) { aObjectToDeviceInv = rObjectToDevice; aObjectToDeviceInv.invert(); } fLineWidth = (aObjectToDeviceInv * basegfx::B2DVector(fLineWidth, 0)).getLength(); } } // try to access buffered data std::shared_ptr pSystemDependentData_Triangulation( rPolygon.getSystemDependentData()); // MM01 need to do line dashing as fallback stuff here now const double fDotDashLength(nullptr != pStroke ? std::accumulate(pStroke->begin(), pStroke->end(), 0.0) : 0.0); const bool bStrokeUsed(0.0 != fDotDashLength); assert(!bStrokeUsed || (bStrokeUsed && pStroke)); if(pSystemDependentData_Triangulation) { // MM01 - check on stroke change. Used against not used, or if oth used, // equal or different? Triangulation geometry creation depends heavily // on stroke, independent of being transformation independent const bool bStrokeWasUsed(!pSystemDependentData_Triangulation->getStroke().empty()); if(bStrokeWasUsed != bStrokeUsed || (bStrokeUsed && *pStroke != pSystemDependentData_Triangulation->getStroke())) { // data invalid, forget pSystemDependentData_Triangulation.reset(); } } if(pSystemDependentData_Triangulation) { // check data validity (I) if(pSystemDependentData_Triangulation->getJoin() != eLineJoin || pSystemDependentData_Triangulation->getCap() != eLineCap || pSystemDependentData_Triangulation->getMiterMinimumAngle() != fMiterMinimumAngle) { // data invalid, forget pSystemDependentData_Triangulation.reset(); } } if(pSystemDependentData_Triangulation) { // check data validity (II) if(pSystemDependentData_Triangulation->getLineWidth() != fLineWidth) { // sometimes small inconsistencies, use a percentage tolerance const double fFactor(basegfx::fTools::equalZero(fLineWidth) ? 0.0 : fabs(1.0 - (pSystemDependentData_Triangulation->getLineWidth() / fLineWidth))); // compare with 5.0% tolerance if(basegfx::fTools::more(fFactor, 0.05)) { // data invalid, forget pSystemDependentData_Triangulation.reset(); } } } if(!pSystemDependentData_Triangulation) { // MM01 need to do line dashing as fallback stuff here now basegfx::B2DPolyPolygon aPolyPolygonLine; if(bStrokeUsed) { // apply LineStyle basegfx::utils::applyLineDashing( rPolygon, // source *pStroke, // pattern &aPolyPolygonLine, // target for lines nullptr, // target for gaps fDotDashLength); // full length if available } else { // no line dashing, just copy aPolyPolygonLine.append(rPolygon); } // try to create data if(bPixelSnapHairline) { // Do NOT transform, but keep device-independent. To // do so, transform to device for snap, but back again after if(!bObjectToDeviceIsIdentity) { aPolyPolygonLine.transform(rObjectToDevice); } aPolyPolygonLine = basegfx::utils::snapPointsOfHorizontalOrVerticalEdges(aPolyPolygonLine); if(!bObjectToDeviceIsIdentity) { if(aObjectToDeviceInv.isIdentity()) { aObjectToDeviceInv = rObjectToDevice; aObjectToDeviceInv.invert(); } aPolyPolygonLine.transform(aObjectToDeviceInv); } } basegfx::triangulator::B2DTriangleVector aTriangles; // MM01 checked/verified for X11 (linux) for(sal_uInt32 a(0); a < aPolyPolygonLine.count(); a++) { const basegfx::B2DPolygon aPolyLine(aPolyPolygonLine.getB2DPolygon(a)); // MM01 upps - commit 51b5b93092d6231615de470c62494c24e54828a1 removed // this *central* geometry-creating lines (!) probably due to aAreaPolyPoly // *not* being used - that's true, but the work is inside of filling // aTriangles data (!) basegfx::utils::createAreaGeometry( aPolyLine, 0.5 * fLineWidth, eLineJoin, eLineCap, basegfx::deg2rad(12.5), 0.4, fMiterMinimumAngle, &aTriangles); // CAUTION! This is *needed* since it creates the data! } if(!aTriangles.empty()) { // Add to buffering mechanism // Add all values the triangulation is based off, too, to check for // validity (see above) pSystemDependentData_Triangulation = rPolygon.addOrReplaceSystemDependentData( ImplGetSystemDependentDataManager(), aTriangles, fLineWidth, eLineJoin, eLineCap, fMiterMinimumAngle, pStroke); } } if(!pSystemDependentData_Triangulation) { return false; } // temporarily adjust brush color to pen color // since the line is drawn as an area-polygon const Color aKeepBrushColor = mnBrushColor; mnBrushColor = mnPenColor; // create the area-polygon for the line const bool bDrawnOk( drawFilledTriangles( rObjectToDevice, pSystemDependentData_Triangulation->getTriangles(), fTransparency)); // restore the original brush GC mnBrushColor = aKeepBrushColor; return bDrawnOk; } Color X11SalGraphicsImpl::getPixel( long nX, long nY ) { if( mrParent.bWindow_ && !mrParent.bVirDev_ ) { XWindowAttributes aAttrib; XGetWindowAttributes( mrParent.GetXDisplay(), mrParent.GetDrawable(), &aAttrib ); if( aAttrib.map_state != IsViewable ) { SAL_WARN( "vcl", "X11SalGraphics::GetPixel drawable not viewable" ); return 0; } } XImage *pXImage = XGetImage( mrParent.GetXDisplay(), mrParent.GetDrawable(), nX, nY, 1, 1, AllPlanes, ZPixmap ); if( !pXImage ) { SAL_WARN( "vcl", "X11SalGraphics::GetPixel !XGetImage()" ); return 0; } XColor aXColor; aXColor.pixel = XGetPixel( pXImage, 0, 0 ); XDestroyImage( pXImage ); return mrParent.GetColormap().GetColor( aXColor.pixel ); } std::shared_ptr X11SalGraphicsImpl::getBitmap( long nX, long nY, long nDX, long nDY ) { bool bFakeWindowBG = false; // normalize if( nDX < 0 ) { nX += nDX; nDX = -nDX; } if ( nDY < 0 ) { nY += nDY; nDY = -nDY; } if( mrParent.bWindow_ && !mrParent.bVirDev_ ) { XWindowAttributes aAttrib; XGetWindowAttributes( mrParent.GetXDisplay(), mrParent.GetDrawable(), &aAttrib ); if( aAttrib.map_state != IsViewable ) bFakeWindowBG = true; else { long nOrgDX = nDX, nOrgDY = nDY; // clip to window size if ( nX < 0 ) { nDX += nX; nX = 0; } if ( nY < 0 ) { nDY += nY; nY = 0; } if( nX + nDX > aAttrib.width ) nDX = aAttrib.width - nX; if( nY + nDY > aAttrib.height ) nDY = aAttrib.height - nY; // inside ? if( nDX <= 0 || nDY <= 0 ) { bFakeWindowBG = true; nDX = nOrgDX; nDY = nOrgDY; } } } std::shared_ptr pSalBitmap = std::make_shared(); sal_uInt16 nBitCount = GetBitCount(); if( &mrParent.GetDisplay()->GetColormap( mrParent.m_nXScreen ) != &mrParent.GetColormap() ) nBitCount = 1; if( ! bFakeWindowBG ) pSalBitmap->ImplCreateFromDrawable( mrParent.GetDrawable(), mrParent.m_nXScreen, nBitCount, nX, nY, nDX, nDY ); else pSalBitmap->Create( Size( nDX, nDY ), (nBitCount > 8) ? 24 : nBitCount, BitmapPalette( nBitCount > 8 ? nBitCount : 0 ) ); return pSalBitmap; } sal_uInt16 X11SalGraphicsImpl::GetBitCount() const { return mrParent.GetVisual().GetDepth(); } long X11SalGraphicsImpl::GetGraphicsWidth() const { if( mrParent.m_pFrame ) return mrParent.m_pFrame->maGeometry.nWidth; else if( mrParent.m_pVDev ) return static_cast< X11SalVirtualDevice* >(mrParent.m_pVDev)->GetWidth(); else return 0; } bool X11SalGraphicsImpl::drawGradient(const tools::PolyPolygon& /*rPolygon*/, const Gradient& /*rGradient*/) { return false; } bool X11SalGraphicsImpl::supportsOperation(OutDevSupportType eType) const { bool bRet = false; switch (eType) { case OutDevSupportType::TransparentRect: case OutDevSupportType::B2DDraw: { XRenderPeer& rPeer = XRenderPeer::GetInstance(); const SalDisplay* pSalDisp = mrParent.GetDisplay(); const SalVisual& rSalVis = pSalDisp->GetVisual(mrParent.GetScreenNumber()); Visual* pDstXVisual = rSalVis.GetVisual(); XRenderPictFormat* pDstVisFmt = rPeer.FindVisualFormat(pDstXVisual); if (pDstVisFmt) bRet = true; } break; default: break; } return bRet; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */