diff options
author | Chris Sherlock <chris.sherlock79@gmail.com> | 2020-05-26 03:53:03 +1000 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2020-05-26 18:27:57 +0200 |
commit | 067342f063fa3ea6773297eea89e45cabbf04a56 (patch) | |
tree | 2d073703bdba099cf0fd25a28159c6ba386511e2 /vcl | |
parent | 414586649582e182b2603702f4f586f4beeed8a9 (diff) |
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@gmail.com>
Diffstat (limited to 'vcl')
-rw-r--r-- | vcl/source/gdi/print2.cxx | 730 |
1 files changed, 342 insertions, 388 deletions
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) { |