diff --git a/drawinglayer/source/primitive2d/controlprimitive2d.cxx b/drawinglayer/source/primitive2d/controlprimitive2d.cxx index 28d5b5d1d1c2..eb8759bb2169 100644 --- a/drawinglayer/source/primitive2d/controlprimitive2d.cxx +++ b/drawinglayer/source/primitive2d/controlprimitive2d.cxx @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -177,21 +178,15 @@ namespace drawinglayer::primitive2d // get bitmap const BitmapEx aContent(aVirtualDevice->GetBitmapEx(Point(), aSizePixel)); - // to avoid scaling, use the Bitmap pixel size as primitive size - const Size aBitmapSize(aContent.GetSizePixel()); - basegfx::B2DVector aBitmapSizeLogic( - rViewInformation.getInverseObjectToViewTransformation() * - basegfx::B2DVector(aBitmapSize.getWidth() - 1, aBitmapSize.getHeight() - 1)); - - if(bScaleUsed) - { - // if scaled adapt to scaled size - aBitmapSizeLogic /= fFactor; - } + // snap translate and scale to discrete position (pixel) to avoid sub-pixel offset and blurring further + basegfx::B2DVector aSnappedTranslate(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aTranslate)); + aSnappedTranslate = rViewInformation.getInverseObjectToViewTransformation() * aSnappedTranslate; + basegfx::B2DVector aSnappedScale(basegfx::fround(rViewInformation.getObjectToViewTransformation() * aScale)); + aSnappedScale = rViewInformation.getInverseObjectToViewTransformation() * aSnappedScale; // short form for scale and translate transformation const basegfx::B2DHomMatrix aBitmapTransform(basegfx::utils::createScaleTranslateB2DHomMatrix( - aBitmapSizeLogic.getX(), aBitmapSizeLogic.getY(), aTranslate.getX(), aTranslate.getY())); + aSnappedScale.getX(), aSnappedScale.getY(), aSnappedTranslate.getX(), aSnappedTranslate.getY())); // create primitive xRetval = new BitmapPrimitive2D( @@ -313,6 +308,25 @@ namespace drawinglayer::primitive2d return aRetval; } + bool ControlPrimitive2D::isVisibleAsChildWindow() const + { + // find out if the control is already visualized as a VCL-ChildWindow + const uno::Reference& rXControl(getXControl()); + + try + { + uno::Reference xControlWindow(rXControl, uno::UNO_QUERY_THROW); + return rXControl->getPeer().is() && xControlWindow->isVisible(); + } + catch (const uno::Exception&) + { + // #i116763# since there is a good alternative when the xControlView + // is not found and it is allowed to happen + } + + return false; + } + void ControlPrimitive2D::get2DDecomposition(Primitive2DDecompositionVisitor& rVisitor, const geometry::ViewInformation2D& rViewInformation) const { // this primitive is view-dependent related to the scaling. If scaling has changed, diff --git a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx index 43f0856fd2f7..588afe4b158f 100644 --- a/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/cairopixelprocessor2d.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -46,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -1199,9 +1201,6 @@ void CairoPixelProcessor2D::paintBitmapAlpha(const BitmapEx& rBitmapEx, cairo_stroke(mpRT); } - const sal_uInt32 nWidth(cairo_image_surface_get_width(pTarget)); - const sal_uInt32 nHeight(cairo_image_surface_get_height(pTarget)); - cairo_set_source_surface(mpRT, pTarget, 0, 0); // get the pattern created by cairo_set_source_surface and @@ -1223,38 +1222,21 @@ void CairoPixelProcessor2D::paintBitmapAlpha(const BitmapEx& rBitmapEx, // page shadows, these DO use 8x1/1x8 images which led me to // that problem. I double-checked that these *are* correctly // defined, that is not the problem. - // I see two solutions: - static bool bRenderMasked(true); + // Decided now to use clipping always. That again is + // simple (we are in unit coordinates) + cairo_rectangle(mpRT, 0, 0, 1, 1); + cairo_clip(mpRT); + cairo_matrix_scale(&aMatrix, cairo_image_surface_get_width(pTarget), + cairo_image_surface_get_height(pTarget)); - if (bRenderMasked) - { - // Consequence is that these need clipping. That again is - // simple (we are in unit coordinates). Only do for RGBA, - // for RGB this effect does not happen - if (CAIRO_FORMAT_ARGB32 == cairo_image_surface_get_format(pTarget)) - { - cairo_rectangle(mpRT, 0, 0, 1, 1); - cairo_clip(mpRT); - } - - cairo_matrix_scale(&aMatrix, nWidth, nHeight); - } - else - { - // Alternative: for RGBA, resize/scale it SLIGHTLY to force - // that half pixel overlap to be inside the unit range. - // That makes the error disappear, so no clip needed, but - // SLIGHTLY smaller. - if (CAIRO_FORMAT_ARGB32 == cairo_image_surface_get_format(pTarget)) - { - cairo_matrix_init_scale(&aMatrix, nWidth + 1, nHeight + 1); - cairo_matrix_translate(&aMatrix, -0.5 / (nWidth + 1), -0.5 / (nHeight + 1)); - } - else - { - cairo_matrix_scale(&aMatrix, nWidth, nHeight); - } - } + // The alternative wpuld be: resize/scale it SLIGHTLY to force + // that half pixel overlap to be inside the unit range. + // That makes the error disappear, so no clip needed, but + // SLIGHTLY smaller. Keeping this code if someone might have + // to finetune this later for reference. + // + // cairo_matrix_init_scale(&aMatrix, nWidth + 1, nHeight + 1); + // cairo_matrix_translate(&aMatrix, -0.5 / (nWidth + 1), -0.5 / (nHeight + 1)); // The error/effect described above also is connected to the // filter used, so I checked the filter modes available @@ -1272,6 +1254,14 @@ void CairoPixelProcessor2D::paintBitmapAlpha(const BitmapEx& rBitmapEx, // to be on the safe side cairo_pattern_set_filter(sourcepattern, CAIRO_FILTER_GOOD); + // also set extend to CAIRO_EXTEND_PAD, else the outside of the + // bitmap is guessed as COL_BLACK and the filtering would blend + // against COL_BLACK what might give strange gray lines at borders + // of white-on-white bitmaps (used e.g. when painting controls). + // NOTE: CAIRO_EXTEND_REPEAT also works with clipping and might be + // broader supported by CVairo implementations + cairo_pattern_set_extend(sourcepattern, CAIRO_EXTEND_PAD); + cairo_pattern_set_matrix(sourcepattern, &aMatrix); // paint bitmap data, evtl. with additional alpha channel @@ -3699,6 +3689,34 @@ void CairoPixelProcessor2D::processSvgRadialGradientPrimitive2D( cairo_restore(mpRT); } +void CairoPixelProcessor2D::processControlPrimitive2D( + const primitive2d::ControlPrimitive2D& rControlPrimitive) +{ + // find out if the control is already visualized as a VCL-ChildWindow + bool bControlIsVisibleAsChildWindow(rControlPrimitive.isVisibleAsChildWindow()); + + // tdf#131281 FormControl rendering for Tiled Rendering + if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) + { + // Do force paint when we are in Tiled Renderer and FormControl is 'visible' + bControlIsVisibleAsChildWindow = false; + } + + if (bControlIsVisibleAsChildWindow) + { + // f the control is already visualized as a VCL-ChildWindow it + // does not need to be painted at all + return; + } + + // process recursively and use the decomposition as Bitmap + // NOTE: The VclPixelProcessor2D tries to paint it using + // UNO API and awt::XView/awt::XGraphics to directly paint the + // control. To do so would need the target OutDev which we + // want to avoid here + process(rControlPrimitive); +} + void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimitive2D& rCandidate) { const cairo_status_t aStart(cairo_status(mpRT)); @@ -3859,6 +3877,12 @@ void CairoPixelProcessor2D::processBasePrimitive2D(const primitive2d::BasePrimit static_cast(rCandidate)); break; } + case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D: + { + processControlPrimitive2D( + static_cast(rCandidate)); + break; + } // continue with decompose default: diff --git a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx index 1581be4a7c42..0ff0ca53c560 100644 --- a/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx +++ b/drawinglayer/source/processor2d/vclpixelprocessor2d.cxx @@ -624,8 +624,38 @@ void VclPixelProcessor2D::processUnifiedTransparencePrimitive2D( void VclPixelProcessor2D::processControlPrimitive2D( const primitive2d::ControlPrimitive2D& rControlPrimitive) { - // control primitive + // find out if the control is already visualized as a VCL-ChildWindow + bool bControlIsVisibleAsChildWindow(rControlPrimitive.isVisibleAsChildWindow()); + + // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple + // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the + // office is in non-layout mode (default for controls at startup). For the common office + // this means that there exists a real VCL-System-Window for the control, so it is *not* + // painted here due to being exactly obscured by that real Window (and creates danger of + // flickering, too). + // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but + // exactly that would be needed on each client displaying the tiles (what would be hard + // to do but also would have advantages - the clients would have real controls in the + // shape of their target system which could be interacted with...). It is also what the + // office does. + // For now, fallback to just render these controls when Tiled Rendering is active to just + // have them displayed on all clients. + if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) + { + // Do force paint when we are in Tiled Renderer and FormControl is 'visible' + bControlIsVisibleAsChildWindow = false; + } + + if (bControlIsVisibleAsChildWindow) + { + // f the control is already visualized as a VCL-ChildWindow it + // does not need to be painted at all + return; + } + + // get awt::XControl from control primitive const uno::Reference& rXControl(rControlPrimitive.getXControl()); + bool bDone(false); try { @@ -636,50 +666,23 @@ void VclPixelProcessor2D::processControlPrimitive2D( if (xNewGraphics.is()) { - // find out if the control is already visualized as a VCL-ChildWindow. If yes, - // it does not need to be painted at all. - uno::Reference xControlWindow(rXControl, uno::UNO_QUERY_THROW); - bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is() - && xControlWindow->isVisible()); + // Needs to be drawn. Link new graphics and view + xControlView->setGraphics(xNewGraphics); - // tdf#131281 The FormControls are not painted when using the Tiled Rendering for a simple - // reason: when e.g. bControlIsVisibleAsChildWindow is true. This is the case because the - // office is in non-layout mode (default for controls at startup). For the common office - // this means that there exists a real VCL-System-Window for the control, so it is *not* - // painted here due to being exactly obscured by that real Window (and creates danger of - // flickering, too). - // Tiled Rendering clients usually do *not* have real VCL-Windows for the controls, but - // exactly that would be needed on each client displaying the tiles (what would be hard - // to do but also would have advantages - the clients would have real controls in the - // shape of their target system which could be interacted with...). It is also what the - // office does. - // For now, fallback to just render these controls when Tiled Rendering is active to just - // have them displayed on all clients. - if (bControlIsVisibleAsChildWindow && comphelper::LibreOfficeKit::isActive()) - { - // Do force paint when we are in Tiled Renderer and FormControl is 'visible' - bControlIsVisibleAsChildWindow = false; - } + // get position + const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation + * rControlPrimitive.getTransform()); + const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); - if (!bControlIsVisibleAsChildWindow) - { - // Needs to be drawn. Link new graphics and view - xControlView->setGraphics(xNewGraphics); + // Do not forget to use the evtl. offsetted origin of the target device, + // e.g. when used with mask/transparence buffer device + const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); + xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), + aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); - // get position - const basegfx::B2DHomMatrix aObjectToPixel(maCurrentTransformation - * rControlPrimitive.getTransform()); - const basegfx::B2DPoint aTopLeftPixel(aObjectToPixel * basegfx::B2DPoint(0.0, 0.0)); - - // Do not forget to use the evtl. offsetted origin of the target device, - // e.g. when used with mask/transparence buffer device - const Point aOrigin(mpOutputDevice->GetMapMode().GetOrigin()); - xControlView->draw(aOrigin.X() + basegfx::fround(aTopLeftPixel.getX()), - aOrigin.Y() + basegfx::fround(aTopLeftPixel.getY())); - - // restore original graphics - xControlView->setGraphics(xOriginalGraphics); - } + // restore original graphics + xControlView->setGraphics(xOriginalGraphics); + bDone = true; } } catch (const uno::Exception&) @@ -687,7 +690,10 @@ void VclPixelProcessor2D::processControlPrimitive2D( // #i116763# removing since there is a good alternative when the xControlView // is not found and it is allowed to happen // DBG_UNHANDLED_EXCEPTION(); + } + if (!bDone) + { // process recursively and use the decomposition as Bitmap process(rControlPrimitive); } diff --git a/include/drawinglayer/primitive2d/controlprimitive2d.hxx b/include/drawinglayer/primitive2d/controlprimitive2d.hxx index 7e1184dae3ae..19226797d659 100644 --- a/include/drawinglayer/primitive2d/controlprimitive2d.hxx +++ b/include/drawinglayer/primitive2d/controlprimitive2d.hxx @@ -93,6 +93,9 @@ public: return mxControlModel; } + /// check if this control is visible as ChildWindow + bool isVisibleAsChildWindow() const; + /** mxControl access. This will on demand create the awt::XControl using createXControl() if it does not exist. It may already have been created or even handed over at incarnation diff --git a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx index 3be88707dd4d..fc67ed78932b 100644 --- a/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx +++ b/include/drawinglayer/processor2d/cairopixelprocessor2d.hxx @@ -41,6 +41,7 @@ class FillGraphicPrimitive2D; class PolyPolygonRGBAPrimitive2D; class PolyPolygonAlphaGradientPrimitive2D; class BitmapAlphaPrimitive2D; +class ControlPrimitive2D; class TextSimplePortionPrimitive2D; class TextDecoratedPortionPrimitive2D; class TextLayouterDevice; @@ -129,6 +130,7 @@ class UNLESS_MERGELIBS(DRAWINGLAYER_DLLPUBLIC) CairoPixelProcessor2D final : pub double fTransparency = 0.0); void processBitmapAlphaPrimitive2D( const primitive2d::BitmapAlphaPrimitive2D& rBitmapAlphaPrimitive2D); + void processControlPrimitive2D(const primitive2d::ControlPrimitive2D& rControlPrimitive); void processTextSimplePortionPrimitive2D( const primitive2d::TextSimplePortionPrimitive2D& rCandidate);