зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1060960: Fix D2D 1.1 backend's ClearRect to deal with transforms and complex clips. r=jrmuizel
This commit is contained in:
Родитель
5a5f0bf600
Коммит
ef8f8d4d27
|
@ -203,9 +203,46 @@ DrawTargetD2D1::ClearRect(const Rect &aRect)
|
|||
{
|
||||
MarkChanged();
|
||||
|
||||
mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
PopAllClips();
|
||||
|
||||
PushClipRect(aRect);
|
||||
|
||||
if (mTransformDirty ||
|
||||
!mTransform.IsIdentity()) {
|
||||
mDC->SetTransform(D2D1::IdentityMatrix());
|
||||
mTransformDirty = true;
|
||||
}
|
||||
|
||||
D2D1_RECT_F clipRect;
|
||||
bool isPixelAligned;
|
||||
if (mTransform.IsRectilinear() &&
|
||||
GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
|
||||
mDC->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
mDC->Clear();
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
||||
PopClip();
|
||||
return;
|
||||
}
|
||||
|
||||
mDC->SetTarget(mTempBitmap);
|
||||
mDC->Clear();
|
||||
|
||||
IntRect addClipRect;
|
||||
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
|
||||
|
||||
RefPtr<ID2D1SolidColorBrush> brush;
|
||||
mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
|
||||
mDC->PushAxisAlignedClip(D2D1::RectF(addClipRect.x, addClipRect.y, addClipRect.XMost(), addClipRect.YMost()), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
|
||||
mDC->FillGeometry(geom, brush);
|
||||
mDC->PopAxisAlignedClip();
|
||||
|
||||
mDC->SetTarget(mBitmap);
|
||||
mDC->DrawImage(mTempBitmap, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_DESTINATION_OUT);
|
||||
|
||||
PopClip();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -466,6 +503,8 @@ DrawTargetD2D1::PushClip(const Path *aPath)
|
|||
return;
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
|
||||
|
||||
PushedClip clip;
|
||||
|
@ -504,6 +543,8 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
|||
return PushClip(path);
|
||||
}
|
||||
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
PushedClip clip;
|
||||
Rect rect = mTransform.TransformBounds(aRect);
|
||||
IntRect intRect;
|
||||
|
@ -525,6 +566,8 @@ DrawTargetD2D1::PushClipRect(const Rect &aRect)
|
|||
void
|
||||
DrawTargetD2D1::PopClip()
|
||||
{
|
||||
mCurrentClippedGeometry = nullptr;
|
||||
|
||||
if (mClipsArePushed) {
|
||||
if (mPushedClips.back().mPath) {
|
||||
mDC->PopLayer();
|
||||
|
@ -829,6 +872,128 @@ DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource)
|
|||
}
|
||||
}
|
||||
|
||||
static D2D1_RECT_F
|
||||
IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
|
||||
{
|
||||
D2D1_RECT_F result;
|
||||
result.left = max(aRect1.left, aRect2.left);
|
||||
result.top = max(aRect1.top, aRect2.top);
|
||||
result.right = min(aRect1.right, aRect2.right);
|
||||
result.bottom = min(aRect1.bottom, aRect2.bottom);
|
||||
|
||||
result.right = max(result.right, result.left);
|
||||
result.bottom = max(result.bottom, result.top);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool
|
||||
DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
|
||||
{
|
||||
if (!mPushedClips.size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
|
||||
for (auto iter = mPushedClips.begin();iter != mPushedClips.end(); iter++) {
|
||||
if (iter->mPath) {
|
||||
return false;
|
||||
}
|
||||
aClipRect = IntersectRect(aClipRect, iter->mBounds);
|
||||
if (!iter->mIsPixelAligned) {
|
||||
aIsPixelAligned = false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
TemporaryRef<ID2D1Geometry>
|
||||
DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
|
||||
{
|
||||
if (mCurrentClippedGeometry) {
|
||||
*aClipBounds = mCurrentClipBounds;
|
||||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
|
||||
|
||||
// if pathGeom is null then pathRect represents the path.
|
||||
RefPtr<ID2D1Geometry> pathGeom;
|
||||
D2D1_RECT_F pathRect;
|
||||
bool pathRectIsAxisAligned = false;
|
||||
auto iter = mPushedClips.begin();
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
} else {
|
||||
pathRect = iter->mBounds;
|
||||
pathRectIsAxisAligned = iter->mIsPixelAligned;
|
||||
}
|
||||
|
||||
iter++;
|
||||
for (;iter != mPushedClips.end(); iter++) {
|
||||
// Do nothing but add it to the current clip bounds.
|
||||
if (!iter->mPath && iter->mIsPixelAligned) {
|
||||
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
||||
IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
|
||||
int32_t(iter->mBounds.right - iter->mBounds.left),
|
||||
int32_t(iter->mBounds.bottom - iter->mBounds.top)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!pathGeom) {
|
||||
if (pathRectIsAxisAligned) {
|
||||
mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
|
||||
IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
|
||||
int32_t(pathRect.right - pathRect.left),
|
||||
int32_t(pathRect.bottom - pathRect.top)));
|
||||
}
|
||||
if (iter->mPath) {
|
||||
// See if pathRect needs to go into the path geometry.
|
||||
if (!pathRectIsAxisAligned) {
|
||||
pathGeom = ConvertRectToGeometry(pathRect);
|
||||
} else {
|
||||
pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
|
||||
}
|
||||
} else {
|
||||
pathRect = IntersectRect(pathRect, iter->mBounds);
|
||||
pathRectIsAxisAligned = false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ID2D1PathGeometry> newGeom;
|
||||
factory()->CreatePathGeometry(byRef(newGeom));
|
||||
|
||||
RefPtr<ID2D1GeometrySink> currentSink;
|
||||
newGeom->Open(byRef(currentSink));
|
||||
|
||||
if (iter->mPath) {
|
||||
pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
|
||||
iter->mTransform, currentSink);
|
||||
} else {
|
||||
RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
|
||||
pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
|
||||
D2D1::IdentityMatrix(), currentSink);
|
||||
}
|
||||
|
||||
currentSink->Close();
|
||||
|
||||
pathGeom = newGeom.forget();
|
||||
}
|
||||
|
||||
// For now we need mCurrentClippedGeometry to always be non-nullptr. This
|
||||
// method might seem a little strange but it is just fine, if pathGeom is
|
||||
// nullptr pathRect will always still contain 1 clip unaccounted for
|
||||
// regardless of mCurrentClipBounds.
|
||||
if (!pathGeom) {
|
||||
pathGeom = ConvertRectToGeometry(pathRect);
|
||||
}
|
||||
mCurrentClippedGeometry = pathGeom.forget();
|
||||
*aClipBounds = mCurrentClipBounds;
|
||||
return mCurrentClippedGeometry;
|
||||
}
|
||||
|
||||
void
|
||||
DrawTargetD2D1::PopAllClips()
|
||||
{
|
||||
|
|
|
@ -171,6 +171,14 @@ private:
|
|||
}
|
||||
void AddDependencyOnSource(SourceSurfaceD2D1* aSource);
|
||||
|
||||
// This returns the clipped geometry, in addition it returns aClipBounds which
|
||||
// represents the intersection of all pixel-aligned rectangular clips that
|
||||
// are currently set. The returned clipped geometry must be clipped by these
|
||||
// bounds to correctly reflect the total clip. This is in device space.
|
||||
TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
|
||||
|
||||
bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
|
||||
|
||||
void PopAllClips();
|
||||
void PushClipsToDC(ID2D1DeviceContext *aDC);
|
||||
void PopClipsFromDC(ID2D1DeviceContext *aDC);
|
||||
|
@ -183,6 +191,7 @@ private:
|
|||
|
||||
RefPtr<ID3D11Device> mDevice;
|
||||
RefPtr<ID3D11Texture2D> mTexture;
|
||||
RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
|
||||
// This is only valid if mCurrentClippedGeometry is non-null. And will
|
||||
// only be the intersection of all pixel-aligned retangular clips. This is in
|
||||
// device space.
|
||||
|
|
Загрузка…
Ссылка в новой задаче