drawinglayer/source/primitive2d/controlprimitive2d.cxx     |   38 +++--
 drawinglayer/source/processor2d/cairopixelprocessor2d.cxx  |   94 +++++++-----
 drawinglayer/source/processor2d/vclpixelprocessor2d.cxx    |   96 ++++++-------
 include/drawinglayer/primitive2d/controlprimitive2d.hxx    |    3 
 include/drawinglayer/processor2d/cairopixelprocessor2d.hxx |    2 
 5 files changed, 141 insertions(+), 92 deletions(-)

New commits:
commit f3f70ef02f04d876abb768bb3ec65645ddb8d89d
Author:     Armin Le Grand (Collabora) <[email protected]>
AuthorDate: Wed Dec 18 15:52:12 2024 +0100
Commit:     Armin Le Grand <[email protected]>
CommitDate: Thu Dec 19 19:04:05 2024 +0100

    tdf#164270 CairoSDPR: Correct Control rendering
    
    Controls need not to be rendered when they are
    visible as Windows (they then render themselves).
    Also added some needed cleanups for better display
    of control primitive decomposition as Bitmap.
    
    Change-Id: I976cd791f9f080a7ab567232303d5d4f0b6dff05
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/178725
    Tested-by: Jenkins
    Reviewed-by: Armin Le Grand <[email protected]>

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 <vcl/virdev.hxx>
 #include <vcl/svapp.hxx>
 #include <com/sun/star/awt/PosSize.hpp>
+#include <com/sun/star/awt/XWindow2.hpp>
 #include <vcl/bitmapex.hxx>
 #include <drawinglayer/primitive2d/bitmapprimitive2d.hxx>
 #include <comphelper/diagnose_ex.hxx>
@@ -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<awt::XControl>& rXControl(getXControl());
+
+            try
+            {
+                uno::Reference<awt::XWindow2> 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 <vcl/cairo.hxx>
 #include <vcl/outdev.hxx>
 #include <vcl/svapp.hxx>
+#include <comphelper/lok.hxx>
 #include <basegfx/polygon/b2dpolygontools.hxx>
 #include <basegfx/polygon/b2dpolypolygontools.hxx>
 #include <drawinglayer/primitive2d/drawinglayer_primitivetypes2d.hxx>
@@ -46,6 +47,7 @@
 #include <drawinglayer/primitive2d/textdecoratedprimitive2d.hxx>
 #include <drawinglayer/primitive2d/shadowprimitive2d.hxx>
 #include <drawinglayer/primitive2d/svggradientprimitive2d.hxx>
+#include <drawinglayer/primitive2d/controlprimitive2d.hxx>
 #include <drawinglayer/converters.hxx>
 #include <drawinglayer/primitive2d/textlayoutdevice.hxx>
 #include <basegfx/curve/b2dcubicbezier.hxx>
@@ -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);
-
-    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);
-        }
-    }
+    // 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));
+
+    // 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<const 
primitive2d::SvgRadialGradientPrimitive2D&>(rCandidate));
             break;
         }
+        case PRIMITIVE2D_ID_CONTROLPRIMITIVE2D:
+        {
+            processControlPrimitive2D(
+                static_cast<const 
primitive2d::ControlPrimitive2D&>(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<awt::XControl>& 
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<awt::XWindow2> xControlWindow(rXControl, 
uno::UNO_QUERY_THROW);
-            bool bControlIsVisibleAsChildWindow(rXControl->getPeer().is()
-                                                && 
xControlWindow->isVisible());
-
-            // 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)
-            {
-                // Needs to be drawn. Link new graphics and view
-                xControlView->setGraphics(xNewGraphics);
-
-                // 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);
-            }
+            // Needs to be drawn. Link new graphics and view
+            xControlView->setGraphics(xNewGraphics);
+
+            // 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);
+            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);

Reply via email to