Bug 1220629 - Part 5: Implement PushLayer/PopLayer API for Direct2D 1.1. r=jrmuizel

This commit is contained in:
Bas Schouten 2016-01-06 00:23:29 +01:00
parent 926a0fbc66
commit f205b39b84
3 changed files with 392 additions and 43 deletions

View File

@ -587,11 +587,7 @@ DrawTargetD2D1::FillGlyphs(ScaledFont *aFont,
D2DFactory()->CreatePathGeometry(getter_AddRefs(path));
RefPtr<ID2D1GeometrySink> 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<ID2D1BitmapBrush> mask;
Matrix maskTransform = aMaskTransform;
RefPtr<ID2D1PathGeometry> clip;
if (aMask) {
mDC->SetTransform(D2D1::IdentityMatrix());
mTransformDirty = true;
RefPtr<ID2D1Image> 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<ID2D1Bitmap> 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<ID2D1GeometrySink> 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<ID2D1CommandList> 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<SourceSurface>
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<ID2D1Bitmap> tmpBitmap;
RefPtr<ID2D1Image> 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<ID2D1BitmapBrush> brush;
if (tmpImage) {
RefPtr<ID2D1ImageBrush> brush;
RefPtr<ID2D1Geometry> 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<ID2D1Effect> blendEffect;
HRESULT hr = mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
if (FAILED(hr) || !mBlendEffect) {
gfxWarning() << "Failed to create blend effect!";
return;
}
}
RefPtr<ID2D1Bitmap> 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<ID2D1Image> 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<ID2D1Image>
DrawTargetD2D1::GetImageForLayerContent()
{
if (!CurrentLayer().mCurrentList) {
RefPtr<ID2D1Bitmap> 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<ID2D1CommandList> 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<ID2D1Geometry>
DrawTargetD2D1::GetClippedGeometry(IntRect *aClipBounds)
{

View File

@ -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<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
const IntSize &aSize,
@ -117,6 +123,7 @@ public:
virtual already_AddRefed<FilterNode> 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<ID2D1Image> GetImageForLayerContent();
ID2D1Image* CurrentTarget()
{
if (CurrentLayer().mCurrentList) {
@ -211,7 +221,6 @@ private:
mutable RefPtr<ID2D1DeviceContext> mDC;
RefPtr<ID2D1Bitmap1> mBitmap;
RefPtr<ID2D1CommandList> mCommandList;
RefPtr<ID2D1Effect> mBlendEffect;
RefPtr<ID2D1SolidColorBrush> mSolidColorBrush;

View File

@ -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<IUnknown*>(this);
return S_OK;
} else if (aIID == IID_ID2D1CommandSink) {
*aPtr = static_cast<ID2D1CommandSink*>(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;
};
}
}