[Libreoffice-commits] core.git: vcl/source

Chris Sherlock (via logerrit) logerrit at kemper.freedesktop.org
Tue May 26 16:28:33 UTC 2020


 vcl/source/gdi/print2.cxx |  730 +++++++++++++++++++++-------------------------
 1 file changed, 342 insertions(+), 388 deletions(-)

New commits:
commit 067342f063fa3ea6773297eea89e45cabbf04a56
Author:     Chris Sherlock <chris.sherlock79 at gmail.com>
AuthorDate: Tue May 26 03:53:03 2020 +1000
Commit:     Tomaž Vajngerl <quikee at gmail.com>
CommitDate: Tue May 26 18:27:57 2020 +0200

    tdf#133216 - Objects are not printed (Win-only)
    
    Seem to have been introduced in commit 6fa6704a7e7c6970b7a7c:6
    "vcl: move functionality out of checkRect(), rendering function obsolete"
    
    This reverts:
    
    commit 7b47a96b20122863e77aa1918e878372b3485c9f.
    "vcl: refactor code into GenerateConnectedComponents()"
    
    commit 5ef0f7dfafd8b83818c831914467f93e47a5bb2f.
    "vcl: refactor code into GenerateIntersectingConnectedComponents()"
    
    commit 93649784ddbb7d9ca779d14fd60fb97385325d17.
    "vcl: move stage 1 functionality into DetectBackground()"
    
    commit 34a699f1894f30f68c3243784586617e01e60ab6.
    "vcl: refactor by creating GetActionAfterBackgroundAction() function"
    
    commit f85769f61b2d2380750be564c6de47f28be35b8a.
    "vcl: refactor by creating RecordMapModeChanges() function"
    
    commit 8318a636336dd6d6b5862a3366f85f96c64d8243.
    "vcl: refactor by creating SetBackgroundColorAndBounds() function"
    
    commit f0ca5a0c447f4fe4667693d744af61eaeb0625ee.
    "vcl: move functionality into FindIncompletelyOccludedBackground()"
    
    commit 449f23c44ccdf6d2bfe7baa143d32d8f585aef4b.
    "vcl: new local function setComponentsSizeAndColor()"
    
    commit 6fa6704a7e7c6970b7a7c695a4a548f8dc693d03.
    "vcl: move functionality out of checkRect(), rendering function obsolete"
    
    Change-Id: Ic85397c1b69f2b529cff90206387d017692cecf2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/94804
    Tested-by: Jenkins
    Reviewed-by: Tomaž Vajngerl <quikee at gmail.com>

diff --git a/vcl/source/gdi/print2.cxx b/vcl/source/gdi/print2.cxx
index 887351fe8e33..89fec06ff466 100644
--- a/vcl/source/gdi/print2.cxx
+++ b/vcl/source/gdi/print2.cxx
@@ -92,18 +92,31 @@ bool DoesActionHandleTransparency( const MetaAction& rAct )
 bool doesRectCoverWithUniformColor(
         tools::Rectangle const & rPrevRect,
         tools::Rectangle const & rCurrRect,
-        VirtualDevice const * pMapModeVDev)
+        OutputDevice const & rMapModeVDev)
 {
     // shape needs to fully cover previous content, and have uniform
     // color
-    return (pMapModeVDev->LogicToPixel(rCurrRect).IsInside(rPrevRect) &&
-        pMapModeVDev->IsFillColor());
+    return (rMapModeVDev.LogicToPixel(rCurrRect).IsInside(rPrevRect) &&
+        rMapModeVDev.IsFillColor());
 }
 
-void setComponentsSizeAndColor(ConnectedComponents &rBackgroundComponent, tools::Rectangle const & rRect, Color const& rColor)
+/** Check whether rCurrRect rectangle fully covers io_rPrevRect - if
+    yes, return true and update o_rBgColor
+ */
+bool checkRect( tools::Rectangle&       io_rPrevRect,
+                       Color&           o_rBgColor,
+                       const tools::Rectangle& rCurrRect,
+                       OutputDevice const &    rMapModeVDev )
 {
-    rBackgroundComponent.aBounds = rRect;
-    rBackgroundComponent.aBgColor = rColor;
+    bool bRet = doesRectCoverWithUniformColor(io_rPrevRect, rCurrRect, rMapModeVDev);
+
+    if( bRet )
+    {
+        io_rPrevRect = rCurrRect;
+        o_rBgColor = rMapModeVDev.GetFillColor();
+    }
+
+    return bRet;
 }
 
 /** #107169# Convert BitmapEx to Bitmap with appropriately blended
@@ -609,440 +622,389 @@ tools::Rectangle ImplCalcActionBounds( const MetaAction& rAct, const OutputDevic
         return tools::Rectangle();
 }
 
-int FindIncompletelyOccludedBackground(ConnectedComponents& rBackgroundComponent, GDIMetaFile const & rMtf, VirtualDevice* pMapModeVDev)
+} // end anon namespace
+
+bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
+                                                     long nMaxBmpDPIX, long nMaxBmpDPIY,
+                                                     bool bReduceTransparency, bool bTransparencyAutoMode,
+                                                     bool bDownsampleBitmaps,
+                                                     const Color& rBackground
+                                                     )
 {
-    MetaAction* pCurrAct=const_cast<GDIMetaFile&>(rMtf).FirstAction();
+    MetaAction*             pCurrAct;
+    bool                    bTransparent( false );
 
-    int nActionNum = 0;
-    int nLastBgAction = -1;
-    bool bStillBackground=true; // true until first non-bg action
+    rOutMtf.Clear();
 
-    while( pCurrAct && bStillBackground )
+    if(!bReduceTransparency || bTransparencyAutoMode)
+        bTransparent = rInMtf.HasTransparentActions();
+
+    // #i10613# Determine set of connected components containing transparent objects. These are
+    // then processed as bitmaps, the original actions are removed from the metafile.
+    if( !bTransparent )
     {
-        switch( pCurrAct->GetType() )
-        {
-            case MetaActionType::RECT:
-            {
-                const tools::Rectangle aRect(
-                    static_cast<const MetaRectAction*>(pCurrAct)->GetRect());
+        // nothing transparent -> just copy
+        rOutMtf = rInMtf;
+    }
+    else
+    {
+        // #i10613#
+        // This works as follows: we want a number of distinct sets of
+        // connected components, where each set contains metafile
+        // actions that are intersecting (note: there are possibly
+        // more actions contained as are directly intersecting,
+        // because we can only produce rectangular bitmaps later
+        // on. Thus, each set of connected components is the smallest
+        // enclosing, axis-aligned rectangle that completely bounds a
+        // number of intersecting metafile actions, plus any action
+        // that would otherwise be cut in two). Therefore, we
+        // iteratively add metafile actions from the original metafile
+        // to this connected components list (aCCList), by checking
+        // each element's bounding box against intersection with the
+        // metaaction at hand.
+        // All those intersecting elements are removed from aCCList
+        // and collected in a temporary list (aCCMergeList). After all
+        // elements have been checked, the aCCMergeList elements are
+        // merged with the metaaction at hand into one resulting
+        // connected component, with one big bounding box, and
+        // inserted into aCCList again.
+        // The time complexity of this algorithm is O(n^3), where n is
+        // the number of metafile actions, and it finds all distinct
+        // regions of rectangle-bounded connected components. This
+        // algorithm was designed by AF.
 
-                if (!doesRectCoverWithUniformColor(rBackgroundComponent.aBounds, aRect, pMapModeVDev))
-                {
-                    setComponentsSizeAndColor(rBackgroundComponent, aRect, pMapModeVDev->GetFillColor());
-                    bStillBackground=false; // incomplete occlusion of background
-                }
-                else
-                {
-                    nLastBgAction=nActionNum; // this _is_ background
-                }
-                break;
-            }
-            case MetaActionType::POLYGON:
-            {
-                const tools::Polygon aPoly(
-                    static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
-                const tools::Rectangle aRect(aPoly.GetBoundRect());
+        //  STAGE 1: Detect background
 
-                if (!basegfx::utils::isRectangle(aPoly.getB2DPolygon()) ||
-                    !doesRectCoverWithUniformColor(rBackgroundComponent.aBounds, aRect, pMapModeVDev))
-                {
-                    setComponentsSizeAndColor(rBackgroundComponent, aRect, pMapModeVDev->GetFillColor());
-                    bStillBackground=false; // incomplete occlusion of background
-                }
-                else
+        // Receives uniform background content, and is _not_ merged
+        // nor checked for intersection against other aCCList elements
+        ConnectedComponents aBackgroundComponent;
+
+        // Read the configuration value of minimal object area where transparency will be removed
+        double fReduceTransparencyMinArea = officecfg::Office::Common::VCL::ReduceTransparencyMinArea::get() / 100.0;
+        SAL_WARN_IF(fReduceTransparencyMinArea > 1.0, "vcl",
+            "Value of ReduceTransparencyMinArea config option is too high");
+        SAL_WARN_IF(fReduceTransparencyMinArea < 0.0, "vcl",
+            "Value of ReduceTransparencyMinArea config option is too low");
+        fReduceTransparencyMinArea = std::clamp(fReduceTransparencyMinArea, 0.0, 1.0);
+
+        // create an OutputDevice to record mapmode changes and the like
+        ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
+        aMapModeVDev->mnDPIX = mnDPIX;
+        aMapModeVDev->mnDPIY = mnDPIY;
+        aMapModeVDev->EnableOutput(false);
+
+        int nLastBgAction, nActionNum;
+
+        // weed out page-filling background objects (if they are
+        // uniformly coloured). Keeping them outside the other
+        // connected components often prevents whole-page bitmap
+        // generation.
+        bool bStillBackground=true; // true until first non-bg action
+        nActionNum=0; nLastBgAction=-1;
+        pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
+        if( rBackground != COL_TRANSPARENT )
+        {
+            aBackgroundComponent.aBgColor = rBackground;
+            aBackgroundComponent.aBounds = GetBackgroundComponentBounds();
+        }
+        while( pCurrAct && bStillBackground )
+        {
+            switch( pCurrAct->GetType() )
+            {
+                case MetaActionType::RECT:
                 {
-                    nLastBgAction=nActionNum; // this _is_ background
+                    if( !checkRect(
+                            aBackgroundComponent.aBounds,
+                            aBackgroundComponent.aBgColor,
+                            static_cast<const MetaRectAction*>(pCurrAct)->GetRect(),
+                            *aMapModeVDev) )
+                        bStillBackground=false; // incomplete occlusion of background
+                    else
+                        nLastBgAction=nActionNum; // this _is_ background
+                    break;
                 }
-                break;
-            }
-            case MetaActionType::POLYPOLYGON:
-            {
-                const tools::PolyPolygon aPoly(
-                    static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
-                const tools::Rectangle aRect(aPoly.GetBoundRect());
-
-                if (aPoly.Count() != 1 ||
-                    !basegfx::utils::isRectangle(aPoly[0].getB2DPolygon()) ||
-                    !doesRectCoverWithUniformColor(rBackgroundComponent.aBounds, aRect, pMapModeVDev))
+                case MetaActionType::POLYGON:
                 {
-                    setComponentsSizeAndColor(rBackgroundComponent, aRect, pMapModeVDev->GetFillColor());
-                    bStillBackground=false; // incomplete occlusion of background
+                    const tools::Polygon aPoly(
+                        static_cast<const MetaPolygonAction*>(pCurrAct)->GetPolygon());
+                    if( !basegfx::utils::isRectangle(
+                            aPoly.getB2DPolygon()) ||
+                        !checkRect(
+                            aBackgroundComponent.aBounds,
+                            aBackgroundComponent.aBgColor,
+                            aPoly.GetBoundRect(),
+                            *aMapModeVDev) )
+                        bStillBackground=false; // incomplete occlusion of background
+                    else
+                        nLastBgAction=nActionNum; // this _is_ background
+                    break;
                 }
-                else
+                case MetaActionType::POLYPOLYGON:
                 {
-                    nLastBgAction=nActionNum; // this _is_ background
+                    const tools::PolyPolygon aPoly(
+                        static_cast<const MetaPolyPolygonAction*>(pCurrAct)->GetPolyPolygon());
+                    if( aPoly.Count() != 1 ||
+                        !basegfx::utils::isRectangle(
+                            aPoly[0].getB2DPolygon()) ||
+                        !checkRect(
+                            aBackgroundComponent.aBounds,
+                            aBackgroundComponent.aBgColor,
+                            aPoly.GetBoundRect(),
+                            *aMapModeVDev) )
+                        bStillBackground=false; // incomplete occlusion of background
+                    else
+                        nLastBgAction=nActionNum; // this _is_ background
+                    break;
                 }
-                break;
-            }
-            case MetaActionType::WALLPAPER:
-            {
-                const tools::Rectangle aRect(
-                    static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect());
-
-                if (!doesRectCoverWithUniformColor(rBackgroundComponent.aBounds, aRect, pMapModeVDev))
+                case MetaActionType::WALLPAPER:
                 {
-                    setComponentsSizeAndColor(rBackgroundComponent, aRect, pMapModeVDev->GetFillColor());
-                    bStillBackground=false; // incomplete occlusion of background
+                    if( !checkRect(
+                            aBackgroundComponent.aBounds,
+                            aBackgroundComponent.aBgColor,
+                            static_cast<const MetaWallpaperAction*>(pCurrAct)->GetRect(),
+                            *aMapModeVDev) )
+                        bStillBackground=false; // incomplete occlusion of background
+                    else
+                        nLastBgAction=nActionNum; // this _is_ background
+                    break;
                 }
-                else
+                default:
                 {
-                    nLastBgAction=nActionNum; // this _is_ background
+                    if( ImplIsNotTransparent( *pCurrAct,
+                                              *aMapModeVDev ) )
+                        bStillBackground=false; // non-transparent action, possibly
+                                                // not uniform
+                    else
+                        // extend current bounds (next uniform action
+                        // needs to fully cover this area)
+                        aBackgroundComponent.aBounds.Union(
+                            ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
+                    break;
                 }
-                break;
-            }
-            default:
-            {
-                if (ImplIsNotTransparent( *pCurrAct, *pMapModeVDev))
-                    bStillBackground=false; // non-transparent action, possibly not uniform
-                else
-                    // extend current bounds (next uniform action needs to fully cover this area)
-                    rBackgroundComponent.aBounds.Union(ImplCalcActionBounds(*pCurrAct, *pMapModeVDev));
-                break;
             }
-        }
 
-        // execute action to get correct MapModes etc.
-        pCurrAct->Execute(pMapModeVDev);
+            // execute action to get correct MapModes etc.
+            pCurrAct->Execute( aMapModeVDev.get() );
 
-        pCurrAct=const_cast<GDIMetaFile&>(rMtf).NextAction();
-        ++nActionNum;
-    }
+            pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
+            ++nActionNum;
+        }
 
-    return nLastBgAction;
-}
+        aMapModeVDev->ClearStack(); // clean up aMapModeVDev
 
-int GetActionAfterBackgroundAction(ConnectedComponents& rBackgroundComponent, MetaAction* pCurrAct,
-                                   GDIMetaFile const & rMtf, int nLastBgAction,
-                                   VirtualDevice* const pMapModeVDev)
-{
-    pMapModeVDev->ClearStack(); // clean up pMapModeVDev
+        // fast-forward until one after the last background action
+        // (need to reconstruct map mode vdev state)
+        nActionNum=0;
+        pCurrAct=const_cast<GDIMetaFile&>(rInMtf).FirstAction();
+        while( pCurrAct && nActionNum<=nLastBgAction )
+        {
+            // up to and including last ink-generating background
+            // action go to background component
+            aBackgroundComponent.aComponentList.emplace_back(
+                    pCurrAct, nActionNum );
+
+            // execute action to get correct MapModes etc.
+            pCurrAct->Execute( aMapModeVDev.get() );
+            pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction();
+            ++nActionNum;
+        }
 
-    // fast-forward until one after the last background action
-    // (need to reconstruct map mode vdev state)
-    int nActionNum=0;
-    pCurrAct=const_cast<GDIMetaFile&>(rMtf).FirstAction();
-    while(pCurrAct && nActionNum <= nLastBgAction)
-    {
-        // up to and including last ink-generating background
-        // action go to background component
-        rBackgroundComponent.aComponentList.emplace_back(pCurrAct, nActionNum);
-
-        // execute action to get correct MapModes etc.
-        pCurrAct->Execute(pMapModeVDev);
-        pCurrAct=const_cast<GDIMetaFile&>(rMtf).NextAction();
-        ++nActionNum;
-    }
+        //  STAGE 2: Generate connected components list
 
-    return nActionNum;
-}
+        ::std::vector<ConnectedComponents> aCCList; // contains distinct sets of connected components as elements.
 
-void RecordMapModeChanges(VirtualDevice* pMapModeVDev, sal_uInt32 nDPIX, sal_uInt32 nDPIY)
-{
-    pMapModeVDev->SetDPIX(nDPIX);
-    pMapModeVDev->SetDPIY(nDPIY);
-    pMapModeVDev->EnableOutput(false);
-}
+        // iterate over all actions (start where background action
+        // search left off)
+        for( ;
+             pCurrAct;
+             pCurrAct=const_cast<GDIMetaFile&>(rInMtf).NextAction(), ++nActionNum )
+        {
+            // execute action to get correct MapModes etc.
+            pCurrAct->Execute( aMapModeVDev.get() );
 
-void SetBackgroundColorAndBounds(ConnectedComponents& rBackgroundComponent, Color const & rBackground, tools::Rectangle const& rBounds)
-{
-    if( rBackground != COL_TRANSPARENT )
-    {
-        rBackgroundComponent.aBgColor = rBackground;
-        rBackgroundComponent.aBounds = rBounds;
-    }
-}
+            // cache bounds of current action
+            const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *aMapModeVDev) );
 
-int DetectBackground(ConnectedComponents& rBackgroundComponent, MetaAction* pCurrAct,
-        Color const & rBackgroundColor, tools::Rectangle const& rRectBounds,
-        GDIMetaFile const & rMtf, VirtualDevice* pMapModeVDev,
-        sal_uInt32 nDPIX, sal_uInt32 nDPIY)
-{
-    RecordMapModeChanges(pMapModeVDev, nDPIX, nDPIY);
-
-    // weed out page-filling background objects (if they are
-    // uniformly coloured). Keeping them outside the other
-    // connected components often prevents whole-page bitmap
-    // generation.
-    SetBackgroundColorAndBounds(rBackgroundComponent, rBackgroundColor, rRectBounds);
-    int nLastBgAction = FindIncompletelyOccludedBackground(rBackgroundComponent, rMtf, pMapModeVDev);
-    int nActionNum = GetActionAfterBackgroundAction(rBackgroundComponent, pCurrAct, rMtf, nLastBgAction, pMapModeVDev);
-    return nActionNum;
-}
+            // accumulate collected bounds here, initialize with current action
+            tools::Rectangle aTotalBounds( aBBCurrAct ); // thus, aTotalComponents.aBounds is empty
+                                                         // for non-output-generating actions
+            bool bTreatSpecial( false );
+            ConnectedComponents aTotalComponents;
 
-bool GenerateIntersectingConnectedComponents(::std::vector<ConnectedComponents>& rConnectedComponents,
-                                             ConnectedComponents& rTotalComponents,
-                                             tools::Rectangle & rTotalBounds, bool bTreatSpecial)
-{
-    bool bSomeComponentsChanged;
-    // now, this is unfortunate: since changing anyone of
-    // the aCCList elements (e.g. by merging or addition
-    // of an action) might generate new intersection with
-    // other aCCList elements, have to repeat the whole
-    // element scanning, until nothing changes anymore.
-    // Thus, this loop here makes us O(n^3) in the worst
-    // case.
-    do
-    {
-        // only loop here if 'intersects' branch below was hit
-        bSomeComponentsChanged = false;
+            //  STAGE 2.1: Search for intersecting cc entries
 
-        // iterate over all current members of aCCList
-        for( auto aCurrCC=rConnectedComponents.begin(); aCurrCC != rConnectedComponents.end(); )
-        {
-            // first check if current element's bounds are
-            // empty. This ensures that empty actions are not
-            // merged into one component, as a matter of fact,
-            // they have no position.
+            // if aBBCurrAct is empty, it will intersect with no
+            // aCCList member. Thus, we can save the check.
+            // Furthermore, this ensures that non-output-generating
+            // actions get their own aCCList entry, which is necessary
+            // when copying them to the output metafile (see stage 4
+            // below).
 
             // #107169# Wholly transparent objects need
             // not be considered for connected components,
             // too. Just put each of them into a separate
             // component.
-            if( !aCurrCC->aBounds.IsEmpty() &&
-                !aCurrCC->bIsFullyTransparent &&
-                aCurrCC->aBounds.IsOver(rTotalBounds))
-            {
-                // union the intersecting aCCList element into aTotalComponents
+            aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *aMapModeVDev);
 
-                // calc union bounding box
-                rTotalBounds.Union( aCurrCC->aBounds );
+            if( !aBBCurrAct.IsEmpty() &&
+                !aTotalComponents.bIsFullyTransparent )
+            {
+                if( !aBackgroundComponent.aComponentList.empty() &&
+                    !aBackgroundComponent.aBounds.IsInside(aTotalBounds) )
+                {
+                    // it seems the background is not large enough. to
+                    // be on the safe side, combine with this component.
+                    aTotalBounds.Union( aBackgroundComponent.aBounds );
 
-                // extract all aCurr actions to aTotalComponents
-                rTotalComponents.aComponentList.splice(rTotalComponents.aComponentList.end(),
-                                                       aCurrCC->aComponentList);
+                    // extract all aCurr actions to aTotalComponents
+                    aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
+                                                            aBackgroundComponent.aComponentList );
 
-                if (aCurrCC->bIsSpecial)
-                    bTreatSpecial = true;
+                    if( aBackgroundComponent.bIsSpecial )
+                        bTreatSpecial = true;
+                }
 
-                // remove and delete aCurrCC element from list (we've now merged its content)
-                aCurrCC = rConnectedComponents.erase(aCurrCC);
+                bool                                    bSomeComponentsChanged;
 
-                // at least one component changed, need to rescan everything
-                bSomeComponentsChanged = true;
-            }
-            else
-            {
-                ++aCurrCC;
-            }
-        }
-    }
-    while(bSomeComponentsChanged);
+                // now, this is unfortunate: since changing anyone of
+                // the aCCList elements (e.g. by merging or addition
+                // of an action) might generate new intersection with
+                // other aCCList elements, have to repeat the whole
+                // element scanning, until nothing changes anymore.
+                // Thus, this loop here makes us O(n^3) in the worst
+                // case.
+                do
+                {
+                    // only loop here if 'intersects' branch below was hit
+                    bSomeComponentsChanged = false;
 
-    return bTreatSpecial;
-}
+                    // iterate over all current members of aCCList
+                    for( auto aCurrCC=aCCList.begin(); aCurrCC != aCCList.end(); )
+                    {
+                        // first check if current element's bounds are
+                        // empty. This ensures that empty actions are not
+                        // merged into one component, as a matter of fact,
+                        // they have no position.
+
+                        // #107169# Wholly transparent objects need
+                        // not be considered for connected components,
+                        // too. Just put each of them into a separate
+                        // component.
+                        if( !aCurrCC->aBounds.IsEmpty() &&
+                            !aCurrCC->bIsFullyTransparent &&
+                            aCurrCC->aBounds.IsOver( aTotalBounds ) )
+                        {
+                            // union the intersecting aCCList element into aTotalComponents
 
-int GenerateConnectedComponents(::std::vector<ConnectedComponents>& rConnectedComponents, ConnectedComponents& rBackgroundComponent, MetaAction* pCurrAct, int nActionNum, GDIMetaFile const & rMtf, VirtualDevice *pMapModeVDev)
-{
-    // iterate over all actions (start where background action search left off)
-    for( ;
-         pCurrAct;
-         pCurrAct=const_cast<GDIMetaFile&>(rMtf).NextAction(), ++nActionNum )
-    {
-        // execute action to get correct MapModes etc.
-        pCurrAct->Execute(pMapModeVDev);
-
-        // cache bounds of current action
-        const tools::Rectangle aBBCurrAct( ImplCalcActionBounds(*pCurrAct, *pMapModeVDev) );
-
-        // accumulate collected bounds here, initialize with current action
-        tools::Rectangle aTotalBounds( aBBCurrAct ); // thus, aTotalComponents.aBounds is empty for
-                                                     // non-output-generating actions
-        bool bTreatSpecial( false );
-        ConnectedComponents aTotalComponents;
-
-        //  STAGE 2.1: Search for intersecting cc entries
-
-        // if aBBCurrAct is empty, it will intersect with no
-        // rConnectedComponentst member. Thus, we can save the check.
-        // Furthermore, this ensures that non-output-generating
-        // actions get their own rConnectedComponents entry, which is necessary
-        // when copying them to the output metafile (see stage 4
-        // below).
-
-        // #107169# Wholly transparent objects need
-        // not be considered for connected components,
-        // too. Just put each of them into a separate
-        // component.
-        aTotalComponents.bIsFullyTransparent = !ImplIsNotTransparent(*pCurrAct, *pMapModeVDev);
-
-        if( !aBBCurrAct.IsEmpty() &&
-            !aTotalComponents.bIsFullyTransparent )
-        {
-            if( !rBackgroundComponent.aComponentList.empty() &&
-                !rBackgroundComponent.aBounds.IsInside(aTotalBounds) )
-            {
-                // it seems the background is not large enough. to
-                // be on the safe side, combine with this component.
-                aTotalBounds.Union(rBackgroundComponent.aBounds);
+                            // calc union bounding box
+                            aTotalBounds.Union( aCurrCC->aBounds );
 
-                // extract all aCurr actions to aTotalComponents
-                aTotalComponents.aComponentList.splice(aTotalComponents.aComponentList.end(),
-                                                       rBackgroundComponent.aComponentList);
+                            // extract all aCurr actions to aTotalComponents
+                            aTotalComponents.aComponentList.splice( aTotalComponents.aComponentList.end(),
+                                                                    aCurrCC->aComponentList );
 
-                if (rBackgroundComponent.bIsSpecial)
-                    bTreatSpecial = true;
-            }
+                            if( aCurrCC->bIsSpecial )
+                                bTreatSpecial = true;
 
-            bTreatSpecial = GenerateIntersectingConnectedComponents(rConnectedComponents, aTotalComponents, aTotalBounds, bTreatSpecial);
-        }
+                            // remove and delete aCurrCC element from list (we've now merged its content)
+                            aCurrCC = aCCList.erase( aCurrCC );
 
-        //  STAGE 2.2: Determine special state for cc element
-
-        // now test whether the whole connected component must be
-        // treated specially (i.e. rendered as a bitmap): if the
-        // added action is the very first action, or all actions
-        // before it are completely transparent, the connected
-        // component need not be treated specially, not even if
-        // the added action contains transparency. This is because
-        // painting of transparent objects on _white background_
-        // works without alpha compositing (you just calculate the
-        // color). Note that for the test "all objects before me
-        // are transparent" no sorting is necessary, since the
-        // added metaaction pCurrAct is always in the order the
-        // metafile is painted. Generally, the order of the
-        // metaactions in the ConnectedComponents are not
-        // guaranteed to be the same as in the metafile.
-        if( bTreatSpecial )
-        {
-            // prev component(s) special -> this one, too
-            aTotalComponents.bIsSpecial = true;
-        }
-        else if(!pCurrAct->IsTransparent())
-        {
-            // added action and none of prev components special ->
-            // this one normal, too
-            aTotalComponents.bIsSpecial = false;
-        }
-        else
-        {
-            // added action is special and none of prev components
-            // special -> do the detailed tests
+                            // at least one component changed, need to rescan everything
+                            bSomeComponentsChanged = true;
+                        }
+                        else
+                        {
+                            ++aCurrCC;
+                        }
+                    }
+                }
+                while( bSomeComponentsChanged );
+            }
 
-            // can the action handle transparency correctly
-            // (i.e. when painted on white background, does the
-            // action still look correct)?
-            if( !DoesActionHandleTransparency( *pCurrAct ) )
+            //  STAGE 2.2: Determine special state for cc element
+
+            // now test whether the whole connected component must be
+            // treated specially (i.e. rendered as a bitmap): if the
+            // added action is the very first action, or all actions
+            // before it are completely transparent, the connected
+            // component need not be treated specially, not even if
+            // the added action contains transparency. This is because
+            // painting of transparent objects on _white background_
+            // works without alpha compositing (you just calculate the
+            // color). Note that for the test "all objects before me
+            // are transparent" no sorting is necessary, since the
+            // added metaaction pCurrAct is always in the order the
+            // metafile is painted. Generally, the order of the
+            // metaactions in the ConnectedComponents are not
+            // guaranteed to be the same as in the metafile.
+            if( bTreatSpecial )
             {
-                // no, action cannot handle its transparency on
-                // a printer device, render to bitmap
+                // prev component(s) special -> this one, too
                 aTotalComponents.bIsSpecial = true;
             }
+            else if(!pCurrAct->IsTransparent())
+            {
+                // added action and none of prev components special ->
+                // this one normal, too
+                aTotalComponents.bIsSpecial = false;
+            }
             else
             {
-                // yes, action can handle its transparency, so
-                // check whether we're on white background
-                if( aTotalComponents.aComponentList.empty() )
+                // added action is special and none of prev components
+                // special -> do the detailed tests
+
+                // can the action handle transparency correctly
+                // (i.e. when painted on white background, does the
+                // action still look correct)?
+                if( !DoesActionHandleTransparency( *pCurrAct ) )
                 {
-                    // nothing between pCurrAct and page
-                    // background -> don't be special
-                    aTotalComponents.bIsSpecial = false;
+                    // no, action cannot handle its transparency on
+                    // a printer device, render to bitmap
+                    aTotalComponents.bIsSpecial = true;
                 }
                 else
                 {
-                    // #107169# Fixes above now ensure that _no_
-                    // object in the list is fully transparent. Thus,
-                    // if the component list is not empty above, we
-                    // must assume that we have to treat this
-                    // component special.
-
-                    // there are non-transparent objects between
-                    // pCurrAct and the empty sheet of paper -> be
-                    // special, then
-                    aTotalComponents.bIsSpecial = true;
+                    // yes, action can handle its transparency, so
+                    // check whether we're on white background
+                    if( aTotalComponents.aComponentList.empty() )
+                    {
+                        // nothing between pCurrAct and page
+                        // background -> don't be special
+                        aTotalComponents.bIsSpecial = false;
+                    }
+                    else
+                    {
+                        // #107169# Fixes above now ensure that _no_
+                        // object in the list is fully transparent. Thus,
+                        // if the component list is not empty above, we
+                        // must assume that we have to treat this
+                        // component special.
+
+                        // there are non-transparent objects between
+                        // pCurrAct and the empty sheet of paper -> be
+                        // special, then
+                        aTotalComponents.bIsSpecial = true;
+                    }
                 }
             }
-        }
 
-        //  STAGE 2.3: Add newly generated CC list element
+            //  STAGE 2.3: Add newly generated CC list element
 
-        // set new bounds and add action to list
-        aTotalComponents.aBounds = aTotalBounds;
-        aTotalComponents.aComponentList.emplace_back(
-                pCurrAct, nActionNum );
+            // set new bounds and add action to list
+            aTotalComponents.aBounds = aTotalBounds;
+            aTotalComponents.aComponentList.emplace_back(
+                    pCurrAct, nActionNum );
 
-        // add aTotalComponents as a new entry to rConnectedComponents
-        rConnectedComponents.push_back( aTotalComponents );
-
-        SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
-                    "Printer::GetPreparedMetaFile empty component" );
-        SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
-                    "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
-        SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
-                    "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
-    }
-
-    return nActionNum;
-}
-
-} // end anon namespace
-
-bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf, GDIMetaFile& rOutMtf,
-                                                     long nMaxBmpDPIX, long nMaxBmpDPIY,
-                                                     bool bReduceTransparency, bool bTransparencyAutoMode,
-                                                     bool bDownsampleBitmaps,
-                                                     const Color& rBackground
-                                                     )
-{
-    MetaAction*             pCurrAct = nullptr;
-    bool                    bTransparent( false );
+            // add aTotalComponents as a new entry to aCCList
+            aCCList.push_back( aTotalComponents );
 
-    rOutMtf.Clear();
-
-    if(!bReduceTransparency || bTransparencyAutoMode)
-        bTransparent = rInMtf.HasTransparentActions();
-
-    // #i10613# Determine set of connected components containing transparent objects. These are
-    // then processed as bitmaps, the original actions are removed from the metafile.
-    if( !bTransparent )
-    {
-        // nothing transparent -> just copy
-        rOutMtf = rInMtf;
-    }
-    else
-    {
-        // #i10613#
-        // This works as follows: we want a number of distinct sets of
-        // connected components, where each set contains metafile
-        // actions that are intersecting (note: there are possibly
-        // more actions contained as are directly intersecting,
-        // because we can only produce rectangular bitmaps later
-        // on. Thus, each set of connected components is the smallest
-        // enclosing, axis-aligned rectangle that completely bounds a
-        // number of intersecting metafile actions, plus any action
-        // that would otherwise be cut in two). Therefore, we
-        // iteratively add metafile actions from the original metafile
-        // to this connected components list (aCCList), by checking
-        // each element's bounding box against intersection with the
-        // metaaction at hand.
-        // All those intersecting elements are removed from aCCList
-        // and collected in a temporary list (aCCMergeList). After all
-        // elements have been checked, the aCCMergeList elements are
-        // merged with the metaaction at hand into one resulting
-        // connected component, with one big bounding box, and
-        // inserted into aCCList again.
-        // The time complexity of this algorithm is O(n^3), where n is
-        // the number of metafile actions, and it finds all distinct
-        // regions of rectangle-bounded connected components. This
-        // algorithm was designed by AF.
-
-        //  STAGE 1: Detect background
-
-        // create an OutputDevice to record mapmode changes and the like
-        ScopedVclPtrInstance< VirtualDevice > aMapModeVDev;
-
-        // Receives uniform background content, and is _not_ merged
-        // nor checked for intersection against other aCCList elements
-        ConnectedComponents aBackgroundComponent;
-
-        int nActionNum = DetectBackground(aBackgroundComponent, pCurrAct,
-                                rBackground, GetBackgroundComponentBounds(),
-                                rInMtf, aMapModeVDev.get(),
-                                mnDPIX, mnDPIY);
-
-        //  STAGE 2: Generate connected components list
-
-        ::std::vector<ConnectedComponents> aCCList; // contains sets of connected components as elements.
-        nActionNum = GenerateConnectedComponents(aCCList, aBackgroundComponent, pCurrAct, nActionNum, rInMtf, aMapModeVDev.get());
+            SAL_WARN_IF( aTotalComponents.aComponentList.empty(), "vcl",
+                        "Printer::GetPreparedMetaFile empty component" );
+            SAL_WARN_IF( aTotalComponents.aBounds.IsEmpty() && (aTotalComponents.aComponentList.size() != 1), "vcl",
+                        "Printer::GetPreparedMetaFile non-output generating actions must be solitary");
+            SAL_WARN_IF( aTotalComponents.bIsFullyTransparent && (aTotalComponents.aComponentList.size() != 1), "vcl",
+                        "Printer::GetPreparedMetaFile fully transparent actions must be solitary");
+        }
 
         // well now, we've got the list of disjunct connected
         // components. Now we've got to create a map, which contains
@@ -1098,14 +1060,6 @@ bool OutputDevice::RemoveTransparenciesFromMetaFile( const GDIMetaFile& rInMtf,
         const tools::Rectangle aOutputRect( aPageOffset, aTmpSize );
         bool bTiling = dynamic_cast<Printer*>(this) != nullptr;
 
-        // Read the configuration value of minimal object area where transparency will be removed
-        double fReduceTransparencyMinArea = officecfg::Office::Common::VCL::ReduceTransparencyMinArea::get() / 100.0;
-        SAL_WARN_IF(fReduceTransparencyMinArea > 1.0, "vcl",
-            "Value of ReduceTransparencyMinArea config option is too high");
-        SAL_WARN_IF(fReduceTransparencyMinArea < 0.0, "vcl",
-            "Value of ReduceTransparencyMinArea config option is too low");
-        fReduceTransparencyMinArea = std::clamp(fReduceTransparencyMinArea, 0.0, 1.0);
-
         // iterate over all aCCList members and generate bitmaps for the special ones
         for (auto & currentItem : aCCList)
         {


More information about the Libreoffice-commits mailing list