diff --git a/gfx/2d/DrawTargetD2D1.cpp b/gfx/2d/DrawTargetD2D1.cpp index 5707e7a3cd6..030df6c7ebc 100644 --- a/gfx/2d/DrawTargetD2D1.cpp +++ b/gfx/2d/DrawTargetD2D1.cpp @@ -587,11 +587,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, D2DFactory()->CreatePathGeometry(getter_AddRefs(path)); RefPtr sink; path->Open(getter_AddRefs(sink)); - sink->BeginFigure(D2D1::Point2F(userRect.left, userRect.top), D2D1_FIGURE_BEGIN_FILLED); - sink->AddLine(D2D1::Point2F(userRect.right, userRect.top)); - sink->AddLine(D2D1::Point2F(userRect.right, userRect.bottom)); - sink->AddLine(D2D1::Point2F(userRect.left, userRect.bottom)); - sink->EndFigure(D2D1_FIGURE_END_CLOSED); + AddRectToSink(sink, userRect); sink->Close(); mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED, @@ -729,6 +725,82 @@ DrawTargetD2D1::PopClip() CurrentLayer().mPushedClips.pop_back(); } +void +DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity, SourceSurface* aMask, + const Matrix& aMaskTransform, const IntRect& aBounds, + bool aCopyBackground) +{ + D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; + + if (aOpaque) { + options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA; + } + if (aCopyBackground) { + options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; + } + + RefPtr mask; + + Matrix maskTransform = aMaskTransform; + + RefPtr clip; + if (aMask) { + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + RefPtr image = GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP); + + // The mask is given in user space. Our layer will apply it in device space. + maskTransform = maskTransform * mTransform; + + if (image) { + RefPtr bitmap; + image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap)); + + mDC->CreateBitmapBrush(bitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)), getter_AddRefs(mask)); + MOZ_ASSERT(bitmap); // This should always be true since it was created for a surface. + + factory()->CreatePathGeometry(getter_AddRefs(clip)); + RefPtr sink; + clip->Open(getter_AddRefs(sink)); + AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width, aMask->GetSize().height)); + sink->Close(); + } else { + gfxCriticalError() << "Failed to get image for mask surface!"; + } + } + + PushAllClips(); + + mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED, D2DMatrix(maskTransform), aOpacity, mask, options), nullptr); + PushedLayer pushedLayer; + pushedLayer.mClipsArePushed = false; + pushedLayer.mIsOpaque = aOpaque; + mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList)); + mPushedLayers.push_back(pushedLayer); + + mDC->SetTarget(CurrentTarget()); +} + +void +DrawTargetD2D1::PopLayer() +{ + MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0); + + RefPtr list = CurrentLayer().mCurrentList; + mPushedLayers.pop_back(); + mDC->SetTarget(CurrentTarget()); + + list->Close(); + mDC->SetTransform(D2D1::IdentityMatrix()); + mTransformDirty = true; + + DCCommandSink sink(mDC); + list->Stream(&sink); + + mDC->PopLayer(); +} + already_AddRefed DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, @@ -1065,31 +1137,23 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) if (D2DSupportsCompositeMode(aOp)) { D2D1_RECT_F rect; bool isAligned; - RefPtr tmpBitmap; + RefPtr tmpImage; bool clipIsComplex = CurrentLayer().mPushedClips.size() && !GetDeviceSpaceClipRect(rect, isAligned); if (clipIsComplex) { if (!IsOperatorBoundByMask(aOp)) { - HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; - // 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); + tmpImage = GetImageForLayerContent(); } } else { PushAllClips(); } mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); - if (tmpBitmap) { - RefPtr brush; + if (tmpImage) { + RefPtr brush; RefPtr inverseGeom = GetInverseClippedGeometry(); - mDC->CreateBitmapBrush(tmpBitmap, getter_AddRefs(brush)); + mDC->CreateImageBrush(tmpImage, D2D1::ImageBrushProperties(D2D1::RectF(0, 0, mSize.width, mSize.height)), + getter_AddRefs(brush)); mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY); mDC->FillGeometry(inverseGeom, brush); @@ -1098,37 +1162,23 @@ DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) return; } - if (!mBlendEffect) { - HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(mBlendEffect)); + RefPtr blendEffect; + HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect)); - if (FAILED(hr) || !mBlendEffect) { - gfxWarning() << "Failed to create blend effect!"; - return; - } - } - - RefPtr tmpBitmap; - HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); - if (FAILED(hr)) { - gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 5CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; + if (FAILED(hr) || !blendEffect) { + gfxWarning() << "Failed to create blend effect!"; return; } - // 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. - mDC->Flush(); + RefPtr tmpImage = GetImageForLayerContent(); - // We need to use a copy here because affects don't accept a surface on - // both their in- and outputs. - tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr); - - mBlendEffect->SetInput(0, tmpBitmap); - mBlendEffect->SetInput(1, source); - mBlendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp)); + blendEffect->SetInput(0, tmpImage); + blendEffect->SetInput(1, source); + blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp)); PushAllClips(); - mDC->DrawImage(mBlendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); + mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); return; } @@ -1205,6 +1255,35 @@ DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAli return true; } +already_AddRefed +DrawTargetD2D1::GetImageForLayerContent() +{ + if (!CurrentLayer().mCurrentList) { + RefPtr tmpBitmap; + HRESULT hr = mDC->CreateBitmap(D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)), getter_AddRefs(tmpBitmap)); + if (FAILED(hr)) { + gfxCriticalError(CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize))) << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr) << " format " << (int)mFormat; + // 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); + return tmpBitmap.forget(); + } else { + RefPtr list = CurrentLayer().mCurrentList; + mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList)); + mDC->SetTarget(CurrentTarget()); + list->Close(); + + DCCommandSink sink(mDC); + list->Stream(&sink); + + return list.forget(); + } +} + already_AddRefed DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds) { diff --git a/gfx/2d/DrawTargetD2D1.h b/gfx/2d/DrawTargetD2D1.h index 78daafb6e9c..70e5efd9e9d 100644 --- a/gfx/2d/DrawTargetD2D1.h +++ b/gfx/2d/DrawTargetD2D1.h @@ -94,6 +94,12 @@ public: virtual void PushClip(const Path *aPath) override; virtual void PushClipRect(const Rect &aRect) override; virtual void PopClip() override; + virtual void PushLayer(bool aOpaque, Float aOpacity, + SourceSurface* aMask, + const Matrix& aMaskTransform, + const IntRect& aBounds = IntRect(), + bool aCopyBackground = false) override; + virtual void PopLayer() override; virtual already_AddRefed CreateSourceSurfaceFromData(unsigned char *aData, const IntSize &aSize, @@ -117,6 +123,7 @@ public: virtual already_AddRefed CreateFilter(FilterType aType) override; virtual bool SupportsRegionClipping() const override { return false; } + virtual bool IsCurrentGroupOpaque() override { return CurrentLayer().mIsOpaque; } virtual void *GetNativeSurface(NativeSurfaceType aType) override { return nullptr; } @@ -167,6 +174,9 @@ private: } void AddDependencyOnSource(SourceSurfaceD2D1* aSource); + // Must be called with all clips popped and an identity matrix set. + already_AddRefed GetImageForLayerContent(); + ID2D1Image* CurrentTarget() { if (CurrentLayer().mCurrentList) { @@ -211,7 +221,6 @@ private: mutable RefPtr mDC; RefPtr mBitmap; RefPtr mCommandList; - RefPtr mBlendEffect; RefPtr mSolidColorBrush; diff --git a/gfx/2d/HelpersD2D.h b/gfx/2d/HelpersD2D.h index 7100ad127fe..bde54fc6507 100644 --- a/gfx/2d/HelpersD2D.h +++ b/gfx/2d/HelpersD2D.h @@ -696,6 +696,267 @@ CreatePartialBitmapForSurface(DataSourceSurface *aSurface, const Matrix &aDestin } } +static inline void AddRectToSink(ID2D1GeometrySink* aSink, const D2D1_RECT_F& aRect) +{ + aSink->BeginFigure(D2D1::Point2F(aRect.left, aRect.top), D2D1_FIGURE_BEGIN_FILLED); + aSink->AddLine(D2D1::Point2F(aRect.right, aRect.top)); + aSink->AddLine(D2D1::Point2F(aRect.right, aRect.bottom)); + aSink->AddLine(D2D1::Point2F(aRect.left, aRect.bottom)); + aSink->EndFigure(D2D1_FIGURE_END_CLOSED); +} + +class DCCommandSink : public ID2D1CommandSink +{ +public: + DCCommandSink(ID2D1DeviceContext* aCtx) : mCtx(aCtx) + { + } + + HRESULT STDMETHODCALLTYPE QueryInterface(const IID &aIID, void **aPtr) + { + if (!aPtr) { + return E_POINTER; + } + + if (aIID == IID_IUnknown) { + *aPtr = static_cast(this); + return S_OK; + } else if (aIID == IID_ID2D1CommandSink) { + *aPtr = static_cast(this); + return S_OK; + } + + return E_NOINTERFACE; + } + + ULONG STDMETHODCALLTYPE AddRef() + { + return 1; + } + + ULONG STDMETHODCALLTYPE Release() + { + return 1; + } + + STDMETHODIMP BeginDraw() + { + // We don't want to do anything here! + return S_OK; + } + STDMETHODIMP EndDraw() + { + // We don't want to do anything here! + return S_OK; + } + + STDMETHODIMP SetAntialiasMode( + D2D1_ANTIALIAS_MODE antialiasMode + ) + { + mCtx->SetAntialiasMode(antialiasMode); + return S_OK; + } + + STDMETHODIMP SetTags(D2D1_TAG tag1, D2D1_TAG tag2) + { + mCtx->SetTags(tag1, tag2); + return S_OK; + } + + STDMETHODIMP SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE textAntialiasMode) + { + mCtx->SetTextAntialiasMode(textAntialiasMode); + return S_OK; + } + + STDMETHODIMP SetTextRenderingParams(_In_opt_ IDWriteRenderingParams *textRenderingParams) + { + mCtx->SetTextRenderingParams(textRenderingParams); + return S_OK; + } + + STDMETHODIMP SetTransform(_In_ CONST D2D1_MATRIX_3X2_F *transform) + { + mCtx->SetTransform(transform); + return S_OK; + } + + STDMETHODIMP SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND primitiveBlend) + { + mCtx->SetPrimitiveBlend(primitiveBlend); + return S_OK; + } + + STDMETHODIMP SetUnitMode(D2D1_UNIT_MODE unitMode) + { + mCtx->SetUnitMode(unitMode); + return S_OK; + } + + STDMETHODIMP Clear(_In_opt_ CONST D2D1_COLOR_F *color) + { + mCtx->Clear(color); + return S_OK; + } + + STDMETHODIMP DrawGlyphRun( + D2D1_POINT_2F baselineOrigin, + _In_ CONST DWRITE_GLYPH_RUN *glyphRun, + _In_opt_ CONST DWRITE_GLYPH_RUN_DESCRIPTION *glyphRunDescription, + _In_ ID2D1Brush *foregroundBrush, + DWRITE_MEASURING_MODE measuringMode + ) + { + mCtx->DrawGlyphRun(baselineOrigin, glyphRun, glyphRunDescription, + foregroundBrush, measuringMode); + return S_OK; + } + + STDMETHODIMP DrawLine( + D2D1_POINT_2F point0, + D2D1_POINT_2F point1, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawLine(point0, point1, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawGeometry( + _In_ ID2D1Geometry *geometry, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawGeometry(geometry, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawRectangle( + _In_ CONST D2D1_RECT_F *rect, + _In_ ID2D1Brush *brush, + FLOAT strokeWidth, + _In_opt_ ID2D1StrokeStyle *strokeStyle + ) + { + mCtx->DrawRectangle(rect, brush, strokeWidth, strokeStyle); + return S_OK; + } + + STDMETHODIMP DrawBitmap( + _In_ ID2D1Bitmap *bitmap, + _In_opt_ CONST D2D1_RECT_F *destinationRectangle, + FLOAT opacity, + D2D1_INTERPOLATION_MODE interpolationMode, + _In_opt_ CONST D2D1_RECT_F *sourceRectangle, + _In_opt_ CONST D2D1_MATRIX_4X4_F *perspectiveTransform + ) + { + mCtx->DrawBitmap(bitmap, destinationRectangle, opacity, + interpolationMode, sourceRectangle, + perspectiveTransform); + return S_OK; + } + + STDMETHODIMP DrawImage( + _In_ ID2D1Image *image, + _In_opt_ CONST D2D1_POINT_2F *targetOffset, + _In_opt_ CONST D2D1_RECT_F *imageRectangle, + D2D1_INTERPOLATION_MODE interpolationMode, + D2D1_COMPOSITE_MODE compositeMode + ) + { + mCtx->DrawImage(image, targetOffset, imageRectangle, + interpolationMode, compositeMode); + return S_OK; + } + + STDMETHODIMP DrawGdiMetafile( + _In_ ID2D1GdiMetafile *gdiMetafile, + _In_opt_ CONST D2D1_POINT_2F *targetOffset + ) + { + mCtx->DrawGdiMetafile(gdiMetafile, targetOffset); + return S_OK; + } + + STDMETHODIMP FillMesh( + _In_ ID2D1Mesh *mesh, + _In_ ID2D1Brush *brush + ) + { + mCtx->FillMesh(mesh, brush); + return S_OK; + } + + STDMETHODIMP FillOpacityMask( + _In_ ID2D1Bitmap *opacityMask, + _In_ ID2D1Brush *brush, + _In_opt_ CONST D2D1_RECT_F *destinationRectangle, + _In_opt_ CONST D2D1_RECT_F *sourceRectangle + ) + { + mCtx->FillOpacityMask(opacityMask, brush, destinationRectangle, + sourceRectangle); + return S_OK; + } + + STDMETHODIMP FillGeometry( + _In_ ID2D1Geometry *geometry, + _In_ ID2D1Brush *brush, + _In_opt_ ID2D1Brush *opacityBrush + ) + { + mCtx->FillGeometry(geometry, brush, opacityBrush); + return S_OK; + } + + STDMETHODIMP FillRectangle( + _In_ CONST D2D1_RECT_F *rect, + _In_ ID2D1Brush *brush + ) + { + mCtx->FillRectangle(rect, brush); + return S_OK; + } + + STDMETHODIMP PushAxisAlignedClip( + _In_ CONST D2D1_RECT_F *clipRect, + D2D1_ANTIALIAS_MODE antialiasMode + ) + { + mCtx->PushAxisAlignedClip(clipRect, antialiasMode); + return S_OK; + } + + STDMETHODIMP PushLayer( + _In_ CONST D2D1_LAYER_PARAMETERS1 *layerParameters1, + _In_opt_ ID2D1Layer *layer + ) + { + mCtx->PushLayer(layerParameters1, layer); + return S_OK; + } + + STDMETHODIMP PopAxisAlignedClip() + { + mCtx->PopAxisAlignedClip(); + return S_OK; + } + + STDMETHODIMP PopLayer() + { + mCtx->PopLayer(); + return S_OK; + } + + ID2D1DeviceContext* mCtx; +}; + } }