зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1029705 - allow clipping to a list of device-space rectangles (a region) in DrawTarget via PushDeviceSpaceClipRects. r=bas
MozReview-Commit-ID: 8zM116zB0NB
This commit is contained in:
Родитель
1e0c767fd6
Коммит
8869fd86cf
10
gfx/2d/2D.h
10
gfx/2d/2D.h
|
@ -1019,6 +1019,16 @@ public:
|
|||
*/
|
||||
virtual void PushClipRect(const Rect &aRect) = 0;
|
||||
|
||||
/**
|
||||
* Push a clip region specifed by the union of axis-aligned rectangular
|
||||
* clips to the DrawTarget. These rectangles are specified in device space.
|
||||
* This must be balanced by a corresponding call to PopClip within a layer.
|
||||
*
|
||||
* @param aRects The rects to clip to
|
||||
* @param aCount The number of rectangles
|
||||
*/
|
||||
virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount);
|
||||
|
||||
/** Pop a clip from the DrawTarget. A pop without a corresponding push will
|
||||
* be ignored.
|
||||
*/
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
#include "2D.h"
|
||||
#include "Logging.h"
|
||||
#include "PathHelpers.h"
|
||||
|
||||
#include "DrawTargetCapture.h"
|
||||
|
||||
|
@ -35,5 +36,21 @@ DrawTarget::DrawCapturedDT(DrawTargetCapture *aCaptureDT,
|
|||
static_cast<DrawTargetCaptureImpl*>(aCaptureDT)->ReplayToDrawTarget(this, aTransform);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTarget::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
|
||||
{
|
||||
Matrix oldTransform = GetTransform();
|
||||
SetTransform(Matrix());
|
||||
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
for (uint32_t i = 0; i < aCount; i++) {
|
||||
AppendRectToPath(pathBuilder, Rect(aRects[i]));
|
||||
}
|
||||
RefPtr<Path> path = pathBuilder->Finish();
|
||||
PushClip(path);
|
||||
|
||||
SetTransform(oldTransform);
|
||||
}
|
||||
|
||||
} // namespace gfx
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -679,22 +679,18 @@ DrawTargetD2D1::Mask(const Pattern &aSource,
|
|||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushClip(const Path *aPath)
|
||||
DrawTargetD2D1::PushClipGeometry(ID2D1Geometry* aGeometry,
|
||||
const D2D1_MATRIX_3X2_F& aTransform,
|
||||
bool aPixelAligned)
|
||||
{
|
||||
if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
|
||||
gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
|
||||
|
||||
PushedClip clip;
|
||||
clip.mTransform = D2DMatrix(mTransform);
|
||||
clip.mPath = pathD2D;
|
||||
|
||||
pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
|
||||
clip.mGeometry = aGeometry;
|
||||
clip.mTransform = aTransform;
|
||||
clip.mIsPixelAligned = aPixelAligned;
|
||||
|
||||
aGeometry->GetBounds(aTransform, &clip.mBounds);
|
||||
|
||||
CurrentLayer().mPushedClips.push_back(clip);
|
||||
|
||||
|
@ -704,10 +700,23 @@ DrawTargetD2D1::PushClip(const Path *aPath)
|
|||
mTransformDirty = true;
|
||||
|
||||
if (CurrentLayer().mClipsArePushed) {
|
||||
PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform);
|
||||
PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushClip(const Path *aPath)
|
||||
{
|
||||
if (aPath->GetBackendType() != BackendType::DIRECT2D1_1) {
|
||||
gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
|
||||
|
||||
PushClipGeometry(pathD2D->GetGeometry(), D2DMatrix(mTransform));
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
||||
{
|
||||
|
@ -715,15 +724,8 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
|||
// Whoops, this isn't a rectangle in device space, Direct2D will not deal
|
||||
// with this transform the way we want it to.
|
||||
// See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
|
||||
|
||||
RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
|
||||
pathBuilder->MoveTo(aRect.TopLeft());
|
||||
pathBuilder->LineTo(aRect.TopRight());
|
||||
pathBuilder->LineTo(aRect.BottomRight());
|
||||
pathBuilder->LineTo(aRect.BottomLeft());
|
||||
pathBuilder->Close();
|
||||
RefPtr<Path> path = pathBuilder->Finish();
|
||||
return PushClip(path);
|
||||
RefPtr<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect));
|
||||
return PushClipGeometry(geom, D2DMatrix(mTransform));
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
@ -746,6 +748,29 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
|
||||
{
|
||||
// Build a path for the union of the rects.
|
||||
RefPtr<ID2D1PathGeometry> path;
|
||||
factory()->CreatePathGeometry(getter_AddRefs(path));
|
||||
RefPtr<ID2D1GeometrySink> sink;
|
||||
path->Open(getter_AddRefs(sink));
|
||||
sink->SetFillMode(D2D1_FILL_MODE_WINDING);
|
||||
for (uint32_t i = 0; i < aCount; i++) {
|
||||
const IntRect& rect = aRects[i];
|
||||
sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
|
||||
D2D1_POINT_2F lines[3] = { D2DPoint(rect.TopRight()), D2DPoint(rect.BottomRight()), D2DPoint(rect.BottomLeft()) };
|
||||
sink->AddLines(lines, 3);
|
||||
sink->EndFigure(D2D1_FIGURE_END_CLOSED);
|
||||
}
|
||||
sink->Close();
|
||||
|
||||
// The path is in device-space, so there is no transform needed,
|
||||
// and all rects are pixel aligned.
|
||||
PushClipGeometry(path, D2D1::IdentityMatrix(), true);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PopClip()
|
||||
{
|
||||
|
@ -756,7 +781,7 @@ DrawTargetD2D1::PopClip()
|
|||
}
|
||||
|
||||
if (CurrentLayer().mClipsArePushed) {
|
||||
if (CurrentLayer().mPushedClips.back().mPath) {
|
||||
if (CurrentLayer().mPushedClips.back().mGeometry) {
|
||||
mDC->PopLayer();
|
||||
} else {
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
@ -1395,7 +1420,7 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAli
|
|||
|
||||
aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
|
||||
for (auto iter = CurrentLayer().mPushedClips.begin();iter != CurrentLayer().mPushedClips.end(); iter++) {
|
||||
if (iter->mPath) {
|
||||
if (iter->mGeometry) {
|
||||
return false;
|
||||
}
|
||||
aClipRect = IntersectRect(aClipRect, iter->mBounds);
|
||||
|
@ -1475,8 +1500,8 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
bool pathRectIsAxisAligned = false;
|
||||
auto iter = CurrentLayer().mPushedClips.begin();
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
if (iter->mGeometry) {
|
||||
pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
|
||||
} else {
|
||||
pathRect = iter->mBounds;
|
||||
pathRectIsAxisAligned = iter->mIsPixelAligned;
|
||||
|
@ -1485,7 +1510,7 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
iter++;
|
||||
for (;iter != CurrentLayer().mPushedClips.end(); iter++) {
|
||||
// Do nothing but add it to the current clip bounds.
|
||||
if (!iter->mPath && iter->mIsPixelAligned) {
|
||||
if (!iter->mGeometry && iter->mIsPixelAligned) {
|
||||
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
||||
IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
|
||||
int32_t(iter->mBounds.right - iter->mBounds.left),
|
||||
|
@ -1500,12 +1525,12 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
int32_t(pathRect.right - pathRect.left),
|
||||
int32_t(pathRect.bottom - pathRect.top)));
|
||||
}
|
||||
if (iter->mPath) {
|
||||
if (iter->mGeometry) {
|
||||
// See if pathRect needs to go into the path geometry.
|
||||
if (!pathRectIsAxisAligned) {
|
||||
pathGeom = ConvertRectToGeometry(pathRect);
|
||||
} else {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
|
||||
}
|
||||
} else {
|
||||
pathRect = IntersectRect(pathRect, iter->mBounds);
|
||||
|
@ -1520,8 +1545,8 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
|||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
newGeom->Open(getter_AddRefs(currentSink));
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
||||
if (iter->mGeometry) {
|
||||
pathGeom->CombineWithGeometry(iter->mGeometry, D2D1_COMBINE_MODE_INTERSECT,
|
||||
iter->mTransform, currentSink);
|
||||
} else {
|
||||
RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
|
||||
|
@ -1592,8 +1617,8 @@ DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC, bool aForceIgnoreAlpha, c
|
|||
mTransformDirty = true;
|
||||
|
||||
for (auto iter = CurrentLayer().mPushedClips.begin(); iter != CurrentLayer().mPushedClips.end(); iter++) {
|
||||
if (iter->mPath) {
|
||||
PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform, aForceIgnoreAlpha, aMaxRect);
|
||||
if (iter->mGeometry) {
|
||||
PushD2DLayer(aDC, iter->mGeometry, iter->mTransform, iter->mIsPixelAligned, aForceIgnoreAlpha, aMaxRect);
|
||||
} else {
|
||||
mDC->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
}
|
||||
|
@ -1604,7 +1629,7 @@ void
|
|||
DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC)
|
||||
{
|
||||
for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
|
||||
if (CurrentLayer().mPushedClips[i].mPath) {
|
||||
if (CurrentLayer().mPushedClips[i].mGeometry) {
|
||||
aDC->PopLayer();
|
||||
} else {
|
||||
aDC->PopAxisAlignedClip();
|
||||
|
@ -1866,7 +1891,7 @@ DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const
|
|||
|
||||
void
|
||||
DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
|
||||
bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
|
||||
bool aPixelAligned, bool aForceIgnoreAlpha, const D2D1_RECT_F& aMaxRect)
|
||||
{
|
||||
D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
|
||||
|
||||
|
@ -1874,8 +1899,10 @@ DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry,
|
|||
options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
|
||||
}
|
||||
|
||||
mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry,
|
||||
D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
|
||||
D2D1_ANTIALIAS_MODE antialias =
|
||||
aPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
|
||||
|
||||
mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias, aTransform,
|
||||
1.0, nullptr, options), nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -93,6 +93,8 @@ public:
|
|||
const DrawOptions &aOptions = DrawOptions()) override;
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect &aRect) override;
|
||||
virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
|
||||
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
|
@ -215,8 +217,11 @@ private:
|
|||
already_AddRefed<ID2D1SolidColorBrush> GetSolidColorBrush(const D2D_COLOR_F& aColor);
|
||||
already_AddRefed<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
|
||||
|
||||
void PushClipGeometry(ID2D1Geometry* aGeometry, const D2D1_MATRIX_3X2_F& aTransform, bool aPixelAligned = false);
|
||||
|
||||
void PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform,
|
||||
bool aForceIgnoreAlpha = false, const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
|
||||
bool aPixelAligned = false, bool aForceIgnoreAlpha = false,
|
||||
const D2D1_RECT_F& aLayerRect = D2D1::InfiniteRect());
|
||||
|
||||
IntSize mSize;
|
||||
|
||||
|
@ -240,13 +245,12 @@ private:
|
|||
struct PushedClip
|
||||
{
|
||||
D2D1_RECT_F mBounds;
|
||||
union {
|
||||
// If mPath is non-null, the mTransform member will be used, otherwise
|
||||
// the mIsPixelAligned member is valid.
|
||||
D2D1_MATRIX_3X2_F mTransform;
|
||||
bool mIsPixelAligned;
|
||||
};
|
||||
RefPtr<PathD2D> mPath;
|
||||
// If mGeometry is non-null, the mTransform member will be used.
|
||||
D2D1_MATRIX_3X2_F mTransform;
|
||||
RefPtr<ID2D1Geometry> mGeometry;
|
||||
// Indicates if mBounds, and when non-null, mGeometry with mTransform
|
||||
// applied, are pixel-aligned.
|
||||
bool mIsPixelAligned;
|
||||
};
|
||||
|
||||
// List of pushed layers.
|
||||
|
|
|
@ -1816,6 +1816,22 @@ DrawTargetSkia::PushClip(const Path *aPath)
|
|||
mCanvas->clipPath(skiaPath->GetPath(), SkRegion::kIntersect_Op, true);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetSkia::PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount)
|
||||
{
|
||||
// Build a region by unioning all the rects together.
|
||||
SkRegion region;
|
||||
for (uint32_t i = 0; i < aCount; i++) {
|
||||
region.op(IntRectToSkIRect(aRects[i]), SkRegion::kUnion_Op);
|
||||
}
|
||||
|
||||
// Clip with the resulting region. clipRegion does not transform
|
||||
// this region by the current transform, unlike the other SkCanvas
|
||||
// clip methods, so it is just passed through in device-space.
|
||||
mCanvas->save();
|
||||
mCanvas->clipRegion(region, SkRegion::kIntersect_Op);
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetSkia::PushClipRect(const Rect& aRect)
|
||||
{
|
||||
|
|
|
@ -104,6 +104,7 @@ public:
|
|||
const Matrix4x4& aMatrix) override;
|
||||
virtual void PushClip(const Path *aPath) override;
|
||||
virtual void PushClipRect(const Rect& aRect) override;
|
||||
virtual void PushDeviceSpaceClipRects(const IntRect* aRects, uint32_t aCount) override;
|
||||
virtual void PopClip() override;
|
||||
virtual void PushLayer(bool aOpaque, Float aOpacity,
|
||||
SourceSurface* aMask,
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "mozilla/gfx/2D.h"
|
||||
#include "mozilla/gfx/DataSurfaceHelpers.h"
|
||||
#include "mozilla/gfx/Logging.h"
|
||||
#include "mozilla/gfx/PathHelpers.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/UniquePtrExtensions.h"
|
||||
|
@ -719,25 +720,41 @@ gfxUtils::ClipToRegion(gfxContext* aContext, const nsIntRegion& aRegion)
|
|||
/*static*/ void
|
||||
gfxUtils::ClipToRegion(DrawTarget* aTarget, const nsIntRegion& aRegion)
|
||||
{
|
||||
if (!aRegion.IsComplex()) {
|
||||
IntRect rect = aRegion.GetBounds();
|
||||
aTarget->PushClipRect(Rect(rect.x, rect.y, rect.width, rect.height));
|
||||
uint32_t numRects = aRegion.GetNumRects();
|
||||
// If there is only one rect, then the region bounds are equivalent to the
|
||||
// contents. So just use push a single clip rect with the bounds.
|
||||
if (numRects == 1) {
|
||||
aTarget->PushClipRect(Rect(aRegion.GetBounds()));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<PathBuilder> pb = aTarget->CreatePathBuilder();
|
||||
|
||||
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
const IntRect& r = iter.Get();
|
||||
pb->MoveTo(Point(r.x, r.y));
|
||||
pb->LineTo(Point(r.XMost(), r.y));
|
||||
pb->LineTo(Point(r.XMost(), r.YMost()));
|
||||
pb->LineTo(Point(r.x, r.YMost()));
|
||||
pb->Close();
|
||||
// Check if the target's transform will preserve axis-alignment and
|
||||
// pixel-alignment for each rect. For now, just handle the common case
|
||||
// of integer translations.
|
||||
Matrix transform = aTarget->GetTransform();
|
||||
if (transform.IsIntegerTranslation()) {
|
||||
IntPoint translation = RoundedToInt(transform.GetTranslation());
|
||||
AutoTArray<IntRect, 16> rects;
|
||||
rects.SetLength(numRects);
|
||||
uint32_t i = 0;
|
||||
// Build the list of transformed rects by adding in the translation.
|
||||
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
IntRect rect = iter.Get();
|
||||
rect.MoveBy(translation);
|
||||
rects[i++] = rect;
|
||||
}
|
||||
aTarget->PushDeviceSpaceClipRects(rects.Elements(), rects.Length());
|
||||
} else {
|
||||
// The transform does not produce axis-aligned rects or a rect was not
|
||||
// pixel-aligned. So just build a path with all the rects and clip to it
|
||||
// instead.
|
||||
RefPtr<PathBuilder> pathBuilder = aTarget->CreatePathBuilder();
|
||||
for (auto iter = aRegion.RectIter(); !iter.Done(); iter.Next()) {
|
||||
AppendRectToPath(pathBuilder, Rect(iter.Get()));
|
||||
}
|
||||
RefPtr<Path> path = pathBuilder->Finish();
|
||||
aTarget->PushClip(path);
|
||||
}
|
||||
RefPtr<Path> path = pb->Finish();
|
||||
|
||||
aTarget->PushClip(path);
|
||||
}
|
||||
|
||||
/*static*/ gfxFloat
|
||||
|
|
Загрузка…
Ссылка в новой задаче