Bug 1151821: Make globalCompositeOperator work correctly when a complex clip is pushed. r=jrmuizel

This commit is contained in:
Bas Schouten 2015-04-10 07:09:31 +02:00
Родитель a5e91c3f1c
Коммит d1703bc5b7
2 изменённых файлов: 79 добавлений и 12 удалений

Просмотреть файл

@ -925,21 +925,22 @@ DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern)
{
MarkChanged();
// It's important to do this before FlushTransformToDC! As this will cause
// the transform to become dirty.
if (!mClipsArePushed) {
mClipsArePushed = true;
PushClipsToDC(mDC);
}
FlushTransformToDC();
if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
// It's important to do this before FlushTransformToDC! As this will cause
// the transform to become dirty.
PushAllClips();
FlushTransformToDC();
return;
}
PopAllClips();
mDC->SetTarget(mTempBitmap);
mDC->Clear(D2D1::ColorF(0, 0));
PushAllClips();
FlushTransformToDC();
}
void
@ -951,6 +952,8 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
return;
}
PopAllClips();
RefPtr<ID2D1Image> image;
mDC->GetTarget(byRef(image));
@ -961,7 +964,38 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
if (patternSupported) {
if (D2DSupportsCompositeMode(aOp)) {
D2D1_RECT_F rect;
bool isAligned;
RefPtr<ID2D1Bitmap> tmpBitmap;
bool clipIsComplex = mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned);
if (clipIsComplex) {
if (!IsOperatorBoundByMask(aOp)) {
HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), byRef(tmpBitmap));
if (FAILED(hr)) {
gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr);
// For now, crash in this scenario; this should happen because tmpBitmap is
// null and CopyFromBitmap call below dereferences it.
// return;
}
mDC->Flush();
tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
}
} else {
PushAllClips();
}
mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp));
if (tmpBitmap) {
RefPtr<ID2D1BitmapBrush> brush;
RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
mDC->CreateBitmapBrush(tmpBitmap, byRef(brush));
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
mDC->FillGeometry(inverseGeom, brush);
mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
}
return;
}
@ -985,7 +1019,6 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
// This flush is important since the copy method will not know about the context drawing to the surface.
// We also need to pop all the clips to make sure any drawn content will have made it to the final bitmap.
PopAllClips();
mDC->Flush();
// We need to use a copy here because affects don't accept a surface on
@ -996,8 +1029,7 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
mBlendEffect->SetInput(1, mTempBitmap);
mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
PushClipsToDC(mDC);
mClipsArePushed = true;
PushAllClips();
mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
return;
@ -1009,6 +1041,8 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
return;
}
PushAllClips();
RefPtr<ID2D1Effect> radialGradientEffect;
mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect));
@ -1078,6 +1112,8 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
return mCurrentClippedGeometry;
}
MOZ_ASSERT(mPushedClips.size());
mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
// if pathGeom is null then pathRect represents the path.
@ -1157,6 +1193,24 @@ DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
return mCurrentClippedGeometry;
}
TemporaryRef<ID2D1Geometry>
DrawTargetD2D1::GetInverseClippedGeometry()
{
IntRect bounds;
RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
RefPtr<ID2D1RectangleGeometry> rectGeom;
RefPtr<ID2D1PathGeometry> inverseGeom;
factory()->CreateRectangleGeometry(D2D1::RectF(0, 0, mSize.width, mSize.height), byRef(rectGeom));
factory()->CreatePathGeometry(byRef(inverseGeom));
RefPtr<ID2D1GeometrySink> sink;
inverseGeom->Open(byRef(sink));
rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE, D2D1::IdentityMatrix(), sink);
sink->Close();
return inverseGeom;
}
void
DrawTargetD2D1::PopAllClips()
{
@ -1167,6 +1221,16 @@ DrawTargetD2D1::PopAllClips()
}
}
void
DrawTargetD2D1::PushAllClips()
{
if (!mClipsArePushed) {
PushClipsToDC(mDC);
mClipsArePushed = true;
}
}
void
DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC)
{

Просмотреть файл

@ -173,9 +173,12 @@ private:
// bounds to correctly reflect the total clip. This is in device space.
TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
TemporaryRef<ID2D1Geometry> GetInverseClippedGeometry();
bool GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned);
void PopAllClips();
void PushAllClips();
void PushClipsToDC(ID2D1DeviceContext *aDC);
void PopClipsFromDC(ID2D1DeviceContext *aDC);