| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| #include <vcl/gdimetafiletools.hxx> |
| #include <vcl/metaact.hxx> |
| #include <basegfx/polygon/b2dpolygonclipper.hxx> |
| #include <basegfx/matrix/b2dhommatrixtools.hxx> |
| #include <basegfx/polygon/b2dpolypolygontools.hxx> |
| #include <basegfx/polygon/b2dpolygontools.hxx> |
| #include <vcl/virdev.hxx> |
| #include <vcl/svapp.hxx> |
| #include <vcl/graphictools.hxx> |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // helpers |
| |
| namespace |
| { |
| bool handleGeometricContent( |
| const basegfx::B2DPolyPolygon& rClip, |
| const basegfx::B2DPolyPolygon& rSource, |
| GDIMetaFile& rTarget, |
| bool bStroke) |
| { |
| if(rSource.count() && rClip.count()) |
| { |
| const basegfx::B2DPolyPolygon aResult( |
| basegfx::tools::clipPolyPolygonOnPolyPolygon( |
| rSource, |
| rClip, |
| true, // inside |
| bStroke)); |
| |
| if(aResult.count()) |
| { |
| if(aResult == rSource) |
| { |
| // not clipped, but inside. Add original |
| return false; |
| } |
| else |
| { |
| // add clipped geometry |
| if(bStroke) |
| { |
| for(sal_uInt32 a(0); a < aResult.count(); a++) |
| { |
| rTarget.AddAction( |
| new MetaPolyLineAction( |
| Polygon(aResult.getB2DPolygon(a)))); |
| } |
| } |
| else |
| { |
| rTarget.AddAction( |
| new MetaPolyPolygonAction( |
| PolyPolygon(aResult))); |
| } |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool handleGradientContent( |
| const basegfx::B2DPolyPolygon& rClip, |
| const basegfx::B2DPolyPolygon& rSource, |
| const Gradient& rGradient, |
| GDIMetaFile& rTarget) |
| { |
| if(rSource.count() && rClip.count()) |
| { |
| const basegfx::B2DPolyPolygon aResult( |
| basegfx::tools::clipPolyPolygonOnPolyPolygon( |
| rSource, |
| rClip, |
| true, // inside |
| false)); // stroke |
| |
| if(aResult.count()) |
| { |
| if(aResult == rSource) |
| { |
| // not clipped, but inside. Add original |
| return false; |
| } |
| else |
| { |
| // add clipped geometry |
| rTarget.AddAction( |
| new MetaGradientExAction( |
| PolyPolygon(aResult), |
| rGradient)); |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| bool handleBitmapContent( |
| const basegfx::B2DPolyPolygon& rClip, |
| const Point& rPoint, |
| const Size& rSize, |
| const BitmapEx& rBitmapEx, |
| GDIMetaFile& rTarget) |
| { |
| if(!rSize.Width() || !rSize.Height() || rBitmapEx.IsEmpty()) |
| { |
| // bitmap or size is empty |
| return true; |
| } |
| |
| const basegfx::B2DRange aLogicBitmapRange( |
| rPoint.X(), rPoint.Y(), |
| rPoint.X() + rSize.Width(), rPoint.Y() + rSize.Height()); |
| const basegfx::B2DPolyPolygon aClipOfBitmap( |
| basegfx::tools::clipPolyPolygonOnRange( |
| rClip, |
| aLogicBitmapRange, |
| true, |
| false)); // stroke |
| |
| if(!aClipOfBitmap.count()) |
| { |
| // outside clip region |
| return true; |
| } |
| |
| // inside or overlapping. Use area to find out if it is completely |
| // covering (inside) or overlapping |
| const double fClipArea(basegfx::tools::getArea(aClipOfBitmap)); |
| const double fBitmapArea( |
| aLogicBitmapRange.getWidth() * aLogicBitmapRange.getWidth() + |
| aLogicBitmapRange.getHeight() * aLogicBitmapRange.getHeight()); |
| const double fFactor(fClipArea / fBitmapArea); |
| |
| if(basegfx::fTools::more(fFactor, 1.0 - 0.001)) |
| { |
| // completely covering (with 0.1% tolerance) |
| return false; |
| } |
| |
| // needs clipping (with 0.1% tolerance). Prepare VirtualDevice |
| // in pixel mode for alpha channel painting (black is transparent, |
| // white to paint 100% opacity) |
| const Size aSizePixel(rBitmapEx.GetSizePixel()); |
| VirtualDevice aVDev; |
| |
| aVDev.SetOutputSizePixel(aSizePixel); |
| aVDev.EnableMapMode(false); |
| aVDev.SetFillColor(COL_WHITE); |
| aVDev.SetLineColor(); |
| |
| if(rBitmapEx.IsTransparent()) |
| { |
| // use given alpha channel |
| aVDev.DrawBitmap(Point(0, 0), rBitmapEx.GetAlpha().GetBitmap()); |
| } |
| else |
| { |
| // reset alpha channel |
| aVDev.SetBackground(Wallpaper(Color(COL_BLACK))); |
| aVDev.Erase(); |
| } |
| |
| // transform polygon from clipping to pixel coordinates |
| basegfx::B2DPolyPolygon aPixelPoly(aClipOfBitmap); |
| basegfx::B2DHomMatrix aTransform; |
| |
| aTransform.translate(-aLogicBitmapRange.getMinX(), -aLogicBitmapRange.getMinY()); |
| aTransform.scale( |
| static_cast< double >(aSizePixel.Width()) / aLogicBitmapRange.getWidth(), |
| static_cast< double >(aSizePixel.Height()) / aLogicBitmapRange.getHeight()); |
| aPixelPoly.transform(aTransform); |
| |
| // to fill the non-covered parts, use the Xor fill rule of |
| // PolyPolygon painting. Start with a all-covering polygon and |
| // add the clip polygon one |
| basegfx::B2DPolyPolygon aInvertPixelPoly; |
| |
| aInvertPixelPoly.append( |
| basegfx::tools::createPolygonFromRect( |
| basegfx::B2DRange( |
| 0.0, 0.0, |
| aSizePixel.Width(), aSizePixel.Height()))); |
| aInvertPixelPoly.append(aPixelPoly); |
| |
| // paint as alpha |
| aVDev.DrawPolyPolygon(aInvertPixelPoly); |
| |
| // get created alpha mask and set defaults |
| AlphaMask aAlpha( |
| aVDev.GetBitmap( |
| Point(0, 0), |
| aSizePixel)); |
| |
| aAlpha.SetPrefSize(rBitmapEx.GetPrefSize()); |
| aAlpha.SetPrefMapMode(rBitmapEx.GetPrefMapMode()); |
| |
| // add new action replacing the old one |
| rTarget.AddAction( |
| new MetaBmpExScaleAction( |
| Point( |
| basegfx::fround(aLogicBitmapRange.getMinX()), |
| basegfx::fround(aLogicBitmapRange.getMinY())), |
| Size( |
| basegfx::fround(aLogicBitmapRange.getWidth()), |
| basegfx::fround(aLogicBitmapRange.getHeight())), |
| BitmapEx(rBitmapEx.GetBitmap(), aAlpha))); |
| |
| return true; |
| } |
| |
| void addSvtGraphicStroke(const SvtGraphicStroke& rStroke, GDIMetaFile& rTarget) |
| { |
| // write SvtGraphicFill |
| SvMemoryStream aMemStm; |
| aMemStm << rStroke; |
| rTarget.AddAction( |
| new MetaCommentAction( |
| "XPATHSTROKE_SEQ_BEGIN", |
| 0, |
| static_cast< const sal_uInt8* >(aMemStm.GetData()), |
| aMemStm.Seek(STREAM_SEEK_TO_END))); |
| } |
| |
| void addSvtGraphicFill(const SvtGraphicFill &rFilling, GDIMetaFile& rTarget) |
| { |
| // write SvtGraphicFill |
| SvMemoryStream aMemStm; |
| aMemStm << rFilling; |
| rTarget.AddAction( |
| new MetaCommentAction( |
| "XPATHFILL_SEQ_BEGIN", |
| 0, |
| static_cast< const sal_uInt8* >(aMemStm.GetData()), |
| aMemStm.Seek(STREAM_SEEK_TO_END))); |
| } |
| } // end of anonymous namespace |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // #121267# Tooling to internally clip geometry against internal clip regions |
| |
| void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource) |
| { |
| const sal_uLong nObjCount(rSource.GetActionCount()); |
| |
| if(!nObjCount) |
| { |
| return; |
| } |
| |
| // prepare target data container and push/pop stack data |
| GDIMetaFile aTarget; |
| bool bChanged(false); |
| std::vector< basegfx::B2DPolyPolygon > aClips; |
| std::vector< sal_uInt16 > aPushFlags; |
| std::vector< MapMode > aMapModes; |
| |
| // start with empty region |
| aClips.push_back(basegfx::B2DPolyPolygon()); |
| |
| // start with default MapMode (MAP_PIXEL) |
| aMapModes.push_back(MapMode()); |
| |
| for(sal_uLong i(0); i < nObjCount; ++i) |
| { |
| const MetaAction* pAction(rSource.GetAction(i)); |
| const sal_uInt16 nType(pAction->GetType()); |
| bool bDone(false); |
| |
| // basic operation takes care of clipregion actions (four) and push/pop of these |
| // to steer the currently set clip region. There *is* an active |
| // clip region when (aClips.size() && aClips.back().count()), see |
| // below |
| switch(nType) |
| { |
| case META_CLIPREGION_ACTION : |
| { |
| const MetaClipRegionAction* pA = static_cast< const MetaClipRegionAction* >(pAction); |
| |
| if(pA->IsClipping()) |
| { |
| const Region& rRegion = pA->GetRegion(); |
| const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); |
| |
| aClips.back() = aNewClip; |
| } |
| else |
| { |
| aClips.back() = basegfx::B2DPolyPolygon(); |
| } |
| |
| break; |
| } |
| |
| case META_ISECTRECTCLIPREGION_ACTION : |
| { |
| const MetaISectRectClipRegionAction* pA = static_cast< const MetaISectRectClipRegionAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(!rRect.IsEmpty() && aClips.size() && aClips.back().count()) |
| { |
| const basegfx::B2DRange aClipRange( |
| rRect.Left(), rRect.Top(), |
| rRect.Right(), rRect.Bottom()); |
| |
| aClips.back() = basegfx::tools::clipPolyPolygonOnRange( |
| aClips.back(), |
| aClipRange, |
| true, // inside |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_ISECTREGIONCLIPREGION_ACTION : |
| { |
| const MetaISectRegionClipRegionAction* pA = static_cast< const MetaISectRegionClipRegionAction* >(pAction); |
| const Region& rRegion = pA->GetRegion(); |
| |
| if(!rRegion.IsEmpty() && aClips.size() && aClips.back().count()) |
| { |
| const basegfx::B2DPolyPolygon aNewClip(rRegion.GetAsB2DPolyPolygon()); |
| |
| aClips.back() = basegfx::tools::clipPolyPolygonOnPolyPolygon( |
| aClips.back(), |
| aNewClip, |
| true, // inside |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_MOVECLIPREGION_ACTION : |
| { |
| const MetaMoveClipRegionAction* pA = static_cast< const MetaMoveClipRegionAction* >(pAction); |
| const long aHorMove(pA->GetHorzMove()); |
| const long aVerMove(pA->GetVertMove()); |
| |
| if((aHorMove || aVerMove) && aClips.size() && aClips.back().count()) |
| { |
| aClips.back().transform( |
| basegfx::tools::createTranslateB2DHomMatrix( |
| aHorMove, |
| aVerMove)); |
| } |
| break; |
| } |
| |
| case META_PUSH_ACTION : |
| { |
| const MetaPushAction* pA = static_cast< const MetaPushAction* >(pAction); |
| const sal_uInt16 nFlags(pA->GetFlags()); |
| |
| aPushFlags.push_back(nFlags); |
| |
| if(nFlags & PUSH_CLIPREGION) |
| { |
| aClips.push_back(aClips.back()); |
| } |
| |
| if(nFlags & PUSH_MAPMODE) |
| { |
| aMapModes.push_back(aMapModes.back()); |
| } |
| break; |
| } |
| |
| case META_POP_ACTION : |
| { |
| |
| if(aPushFlags.size()) |
| { |
| const sal_uInt16 nFlags(aPushFlags.back()); |
| aPushFlags.pop_back(); |
| |
| if(nFlags & PUSH_CLIPREGION) |
| { |
| if(aClips.size() > 1) |
| { |
| aClips.pop_back(); |
| } |
| else |
| { |
| OSL_ENSURE(false, "Wrong POP() in ClipRegions (!)"); |
| } |
| } |
| |
| if(nFlags & PUSH_MAPMODE) |
| { |
| if(aMapModes.size() > 1) |
| { |
| aMapModes.pop_back(); |
| } |
| else |
| { |
| OSL_ENSURE(false, "Wrong POP() in MapModes (!)"); |
| } |
| } |
| } |
| else |
| { |
| OSL_ENSURE(false, "Invalid pop() without push() (!)"); |
| } |
| |
| break; |
| } |
| |
| case META_MAPMODE_ACTION : |
| { |
| const MetaMapModeAction* pA = static_cast< const MetaMapModeAction* >(pAction); |
| |
| aMapModes.back() = pA->GetMapMode(); |
| break; |
| } |
| |
| default: |
| { |
| break; |
| } |
| } |
| |
| // this area contains all actions which could potentially be clipped. Since |
| // this tooling is only a fallback (see comments in header), only the needed |
| // actions will be implemented. Extend using the pattern for the already |
| // implemented actions. |
| if(aClips.size() && aClips.back().count()) |
| { |
| switch(nType) |
| { |
| // |
| // pixel actions, just check on inside |
| // |
| case META_PIXEL_ACTION : |
| { |
| const MetaPixelAction* pA = static_cast< const MetaPixelAction* >(pAction); |
| const Point& rPoint = pA->GetPoint(); |
| |
| if(!basegfx::tools::isInside( |
| aClips.back(), |
| basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) |
| { |
| // when not inside, do not add original |
| bDone = true; |
| } |
| break; |
| } |
| |
| case META_POINT_ACTION : |
| { |
| const MetaPointAction* pA = static_cast< const MetaPointAction* >(pAction); |
| const Point& rPoint = pA->GetPoint(); |
| |
| if(!basegfx::tools::isInside( |
| aClips.back(), |
| basegfx::B2DPoint(rPoint.X(), rPoint.Y()))) |
| { |
| // when not inside, do not add original |
| bDone = true; |
| } |
| break; |
| } |
| |
| // |
| // geometry actions |
| // |
| case META_LINE_ACTION : |
| { |
| const MetaLineAction* pA = static_cast< const MetaLineAction* >(pAction); |
| const Point& rStart(pA->GetStartPoint()); |
| const Point& rEnd(pA->GetEndPoint()); |
| basegfx::B2DPolygon aLine; |
| |
| aLine.append(basegfx::B2DPoint(rStart.X(), rStart.Y())); |
| aLine.append(basegfx::B2DPoint(rEnd.X(), rEnd.Y())); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(aLine), |
| aTarget, |
| true); // stroke |
| break; |
| } |
| |
| case META_RECT_ACTION : |
| { |
| const MetaRectAction* pA = static_cast< const MetaRectAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon( |
| basegfx::tools::createPolygonFromRect( |
| basegfx::B2DRange( |
| rRect.Left(), rRect.Top(), |
| rRect.Right(), rRect.Bottom()))), |
| aTarget, |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_ROUNDRECT_ACTION : |
| { |
| const MetaRoundRectAction* pA = static_cast< const MetaRoundRectAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| const sal_uInt32 nHor(pA->GetHorzRound()); |
| const sal_uInt32 nVer(pA->GetVertRound()); |
| const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); |
| basegfx::B2DPolygon aOutline; |
| |
| if(nHor || nVer) |
| { |
| double fRadiusX((nHor * 2.0) / (aRange.getWidth() > 0.0 ? aRange.getWidth() : 1.0)); |
| double fRadiusY((nVer * 2.0) / (aRange.getHeight() > 0.0 ? aRange.getHeight() : 1.0)); |
| fRadiusX = std::max(0.0, std::min(1.0, fRadiusX)); |
| fRadiusY = std::max(0.0, std::min(1.0, fRadiusY)); |
| |
| aOutline = basegfx::tools::createPolygonFromRect(aRange, fRadiusX, fRadiusY); |
| } |
| else |
| { |
| aOutline = basegfx::tools::createPolygonFromRect(aRange); |
| } |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(aOutline), |
| aTarget, |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_ELLIPSE_ACTION : |
| { |
| const MetaEllipseAction* pA = static_cast< const MetaEllipseAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| const basegfx::B2DRange aRange(rRect.Left(), rRect.Top(), rRect.Right(), rRect.Bottom()); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon( |
| basegfx::tools::createPolygonFromEllipse( |
| aRange.getCenter(), |
| aRange.getWidth() * 0.5, |
| aRange.getHeight() * 0.5)), |
| aTarget, |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_ARC_ACTION : |
| { |
| const MetaArcAction* pA = static_cast< const MetaArcAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| const Polygon aToolsPoly( |
| rRect, |
| pA->GetStartPoint(), |
| pA->GetEndPoint(), |
| POLY_ARC); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), |
| aTarget, |
| true); // stroke |
| } |
| break; |
| } |
| |
| case META_PIE_ACTION : |
| { |
| const MetaPieAction* pA = static_cast< const MetaPieAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| const Polygon aToolsPoly( |
| rRect, |
| pA->GetStartPoint(), |
| pA->GetEndPoint(), |
| POLY_PIE); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), |
| aTarget, |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_CHORD_ACTION : |
| { |
| const MetaChordAction* pA = static_cast< const MetaChordAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| const Polygon aToolsPoly( |
| rRect, |
| pA->GetStartPoint(), |
| pA->GetEndPoint(), |
| POLY_CHORD); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(aToolsPoly.getB2DPolygon()), |
| aTarget, |
| false); // stroke |
| } |
| break; |
| } |
| |
| case META_POLYLINE_ACTION : |
| { |
| const MetaPolyLineAction* pA = static_cast< const MetaPolyLineAction* >(pAction); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), |
| aTarget, |
| true); // stroke |
| break; |
| } |
| |
| case META_POLYGON_ACTION : |
| { |
| const MetaPolygonAction* pA = static_cast< const MetaPolygonAction* >(pAction); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon(pA->GetPolygon().getB2DPolygon()), |
| aTarget, |
| false); // stroke |
| break; |
| } |
| |
| case META_POLYPOLYGON_ACTION : |
| { |
| const MetaPolyPolygonAction* pA = static_cast< const MetaPolyPolygonAction* >(pAction); |
| const PolyPolygon& rPoly = pA->GetPolyPolygon(); |
| |
| bDone = handleGeometricContent( |
| aClips.back(), |
| rPoly.getB2DPolyPolygon(), |
| aTarget, |
| false); // stroke |
| break; |
| } |
| |
| // |
| // bitmap actions, create BitmapEx with alpha channel derived |
| // from clipping |
| // |
| case META_BMPEX_ACTION : |
| { |
| const MetaBmpExAction* pA = static_cast< const MetaBmpExAction* >(pAction); |
| const BitmapEx& rBitmapEx = pA->GetBitmapEx(); |
| |
| // the logical size depends on the PrefSize of the given bitmap in |
| // combination with the current MapMode |
| Size aLogicalSize(rBitmapEx.GetPrefSize()); |
| |
| if(MAP_PIXEL == rBitmapEx.GetPrefMapMode().GetMapUnit()) |
| { |
| aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); |
| } |
| else |
| { |
| aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmapEx.GetPrefMapMode(), aMapModes.back().GetMapUnit()); |
| } |
| |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetPoint(), |
| aLogicalSize, |
| rBitmapEx, |
| aTarget); |
| break; |
| } |
| |
| case META_BMP_ACTION : |
| { |
| const MetaBmpAction* pA = static_cast< const MetaBmpAction* >(pAction); |
| const Bitmap& rBitmap = pA->GetBitmap(); |
| |
| // the logical size depends on the PrefSize of the given bitmap in |
| // combination with the current MapMode |
| Size aLogicalSize(rBitmap.GetPrefSize()); |
| |
| if(MAP_PIXEL == rBitmap.GetPrefMapMode().GetMapUnit()) |
| { |
| aLogicalSize = Application::GetDefaultDevice()->PixelToLogic(aLogicalSize, aMapModes.back().GetMapUnit()); |
| } |
| else |
| { |
| aLogicalSize = OutputDevice::LogicToLogic(aLogicalSize, rBitmap.GetPrefMapMode(), aMapModes.back().GetMapUnit()); |
| } |
| |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetPoint(), |
| aLogicalSize, |
| BitmapEx(rBitmap), |
| aTarget); |
| break; |
| } |
| |
| case META_BMPEXSCALE_ACTION : |
| { |
| const MetaBmpExScaleAction* pA = static_cast< const MetaBmpExScaleAction* >(pAction); |
| |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetPoint(), |
| pA->GetSize(), |
| pA->GetBitmapEx(), |
| aTarget); |
| break; |
| } |
| |
| case META_BMPSCALE_ACTION : |
| { |
| const MetaBmpScaleAction* pA = static_cast< const MetaBmpScaleAction* >(pAction); |
| |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetPoint(), |
| pA->GetSize(), |
| BitmapEx(pA->GetBitmap()), |
| aTarget); |
| break; |
| } |
| |
| case META_BMPEXSCALEPART_ACTION : |
| { |
| const MetaBmpExScalePartAction* pA = static_cast< const MetaBmpExScalePartAction* >(pAction); |
| const BitmapEx& rBitmapEx = pA->GetBitmapEx(); |
| |
| if(rBitmapEx.IsEmpty()) |
| { |
| // empty content |
| bDone = true; |
| } |
| else |
| { |
| BitmapEx aCroppedBitmapEx(rBitmapEx); |
| const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); |
| |
| if(aCropRectangle.IsEmpty()) |
| { |
| // empty content |
| bDone = true; |
| } |
| else |
| { |
| aCroppedBitmapEx.Crop(aCropRectangle); |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetDestPoint(), |
| pA->GetDestSize(), |
| aCroppedBitmapEx, |
| aTarget); |
| } |
| } |
| break; |
| } |
| |
| case META_BMPSCALEPART_ACTION : |
| { |
| const MetaBmpScalePartAction* pA = static_cast< const MetaBmpScalePartAction* >(pAction); |
| const Bitmap& rBitmap = pA->GetBitmap(); |
| |
| if(rBitmap.IsEmpty()) |
| { |
| // empty content |
| bDone = true; |
| } |
| else |
| { |
| Bitmap aCroppedBitmap(rBitmap); |
| const Rectangle aCropRectangle(pA->GetSrcPoint(), pA->GetSrcSize()); |
| |
| if(aCropRectangle.IsEmpty()) |
| { |
| // empty content |
| bDone = true; |
| } |
| else |
| { |
| aCroppedBitmap.Crop(aCropRectangle); |
| bDone = handleBitmapContent( |
| aClips.back(), |
| pA->GetDestPoint(), |
| pA->GetDestSize(), |
| BitmapEx(aCroppedBitmap), |
| aTarget); |
| } |
| } |
| break; |
| } |
| |
| // |
| // need to handle all those 'hacks' which hide data in comments |
| // |
| case META_COMMENT_ACTION : |
| { |
| const MetaCommentAction* pA = static_cast< const MetaCommentAction* >(pAction); |
| const ByteString& rComment = pA->GetComment(); |
| |
| if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XGRAD_SEQ_BEGIN")) |
| { |
| // nothing to do; this just means that between here and XGRAD_SEQ_END |
| // exists a META_GRADIENTEX_ACTION mixed with Xor-tricked painiting |
| // commands. This comment is used to scan over these and filter for |
| // the gradient action. It is needed to support META_GRADIENTEX_ACTION |
| // in this processor to solve usages. |
| } |
| else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHFILL_SEQ_BEGIN")) |
| { |
| SvtGraphicFill aFilling; |
| PolyPolygon aPath; |
| |
| { // read SvtGraphicFill |
| SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); |
| aMemStm >> aFilling; |
| } |
| |
| aFilling.getPath(aPath); |
| |
| if(aPath.Count()) |
| { |
| const basegfx::B2DPolyPolygon aSource(aPath.getB2DPolyPolygon()); |
| const basegfx::B2DPolyPolygon aResult( |
| basegfx::tools::clipPolyPolygonOnPolyPolygon( |
| aSource, |
| aClips.back(), |
| true, // inside |
| false)); // stroke |
| |
| if(aResult.count()) |
| { |
| if(aResult != aSource) |
| { |
| // add clipped geometry |
| aFilling.setPath(PolyPolygon(aResult)); |
| addSvtGraphicFill(aFilling, aTarget); |
| bDone = true; |
| } |
| } |
| else |
| { |
| // exchange with empty polygon |
| aFilling.setPath(PolyPolygon()); |
| addSvtGraphicFill(aFilling, aTarget); |
| bDone = true; |
| } |
| } |
| } |
| else if(COMPARE_EQUAL == rComment.CompareIgnoreCaseToAscii("XPATHSTROKE_SEQ_BEGIN")) |
| { |
| SvtGraphicStroke aStroke; |
| Polygon aPath; |
| |
| { // read SvtGraphicFill |
| SvMemoryStream aMemStm((void*)pA->GetData(), pA->GetDataSize(),STREAM_READ); |
| aMemStm >> aStroke; |
| } |
| |
| aStroke.getPath(aPath); |
| |
| if(aPath.GetSize()) |
| { |
| const basegfx::B2DPolygon aSource(aPath.getB2DPolygon()); |
| const basegfx::B2DPolyPolygon aResult( |
| basegfx::tools::clipPolygonOnPolyPolygon( |
| aSource, |
| aClips.back(), |
| true, // inside |
| true)); // stroke |
| |
| if(aResult.count()) |
| { |
| if(aResult.count() > 1 || aResult.getB2DPolygon(0) != aSource) |
| { |
| // add clipped geometry |
| for(sal_uInt32 a(0); a < aResult.count(); a++) |
| { |
| aStroke.setPath(Polygon(aResult.getB2DPolygon(a))); |
| addSvtGraphicStroke(aStroke, aTarget); |
| } |
| |
| bDone = true; |
| } |
| } |
| else |
| { |
| // exchange with empty polygon |
| aStroke.setPath(Polygon()); |
| addSvtGraphicStroke(aStroke, aTarget); |
| bDone = true; |
| } |
| |
| } |
| } |
| break; |
| } |
| |
| // |
| // need to handle gradient fills (hopefully only unroated ones) |
| // |
| |
| case META_GRADIENT_ACTION : |
| { |
| const MetaGradientAction* pA = static_cast< const MetaGradientAction* >(pAction); |
| const Rectangle& rRect = pA->GetRect(); |
| |
| if(rRect.IsEmpty()) |
| { |
| bDone = true; |
| } |
| else |
| { |
| bDone = handleGradientContent( |
| aClips.back(), |
| basegfx::B2DPolyPolygon( |
| basegfx::tools::createPolygonFromRect( |
| basegfx::B2DRange( |
| rRect.Left(), rRect.Top(), |
| rRect.Right(), rRect.Bottom()))), |
| pA->GetGradient(), |
| aTarget); |
| } |
| |
| |
| break; |
| } |
| |
| case META_GRADIENTEX_ACTION : |
| { |
| const MetaGradientExAction* pA = static_cast< const MetaGradientExAction* >(pAction); |
| const PolyPolygon& rPolyPoly = pA->GetPolyPolygon(); |
| |
| bDone = handleGradientContent( |
| aClips.back(), |
| rPolyPoly.getB2DPolyPolygon(), |
| pA->GetGradient(), |
| aTarget); |
| break; |
| } |
| |
| // not (yet) supported actions |
| // |
| // META_NULL_ACTION |
| // META_TEXT_ACTION |
| // META_TEXTARRAY_ACTION |
| // META_STRETCHTEXT_ACTION |
| // META_TEXTRECT_ACTION |
| // META_MASK_ACTION |
| // META_MASKSCALE_ACTION |
| // META_MASKSCALEPART_ACTION |
| // META_HATCH_ACTION |
| // META_WALLPAPER_ACTION |
| // META_FILLCOLOR_ACTION |
| // META_TEXTCOLOR_ACTION |
| // META_TEXTFILLCOLOR_ACTION |
| // META_TEXTALIGN_ACTION |
| // META_MAPMODE_ACTION |
| // META_FONT_ACTION |
| // META_TRANSPARENT_ACTION |
| // META_EPS_ACTION |
| // META_REFPOINT_ACTION |
| // META_TEXTLINECOLOR_ACTION |
| // META_TEXTLINE_ACTION |
| // META_FLOATTRANSPARENT_ACTION |
| // META_LAYOUTMODE_ACTION |
| // META_TEXTLANGUAGE_ACTION |
| // META_OVERLINECOLOR_ACTION |
| |
| // if an action is not handled at all, it will simply get copied to the |
| // target (see below). This is the default for all non-implemented actions |
| default: |
| { |
| break; |
| } |
| } |
| } |
| |
| if(bDone) |
| { |
| bChanged = true; |
| } |
| else |
| { |
| const_cast< MetaAction* >(pAction)->Duplicate(); |
| aTarget.AddAction(const_cast< MetaAction* >(pAction)); |
| } |
| } |
| |
| if(bChanged) |
| { |
| // when changed, copy back and do not forget to set MapMode |
| // and PrefSize |
| aTarget.SetPrefMapMode(rSource.GetPrefMapMode()); |
| aTarget.SetPrefSize(rSource.GetPrefSize()); |
| rSource = aTarget; |
| } |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| |
| bool VCL_DLLPUBLIC usesClipActions(const GDIMetaFile& rSource) |
| { |
| const sal_uLong nObjCount(rSource.GetActionCount()); |
| |
| for(sal_uLong i(0); i < nObjCount; ++i) |
| { |
| const MetaAction* pAction(rSource.GetAction(i)); |
| const sal_uInt16 nType(pAction->GetType()); |
| |
| switch(nType) |
| { |
| case META_CLIPREGION_ACTION : |
| case META_ISECTRECTCLIPREGION_ACTION : |
| case META_ISECTREGIONCLIPREGION_ACTION : |
| case META_MOVECLIPREGION_ACTION : |
| { |
| return true; |
| break; |
| } |
| |
| default: break; |
| } |
| } |
| |
| return false; |
| } |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // eof |