michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include michael@0: #include "DrawTargetD2D.h" michael@0: #include "SourceSurfaceD2D.h" michael@0: #ifdef USE_D2D1_1 michael@0: #include "SourceSurfaceD2D1.h" michael@0: #endif michael@0: #include "SourceSurfaceD2DTarget.h" michael@0: #include "ShadersD2D.h" michael@0: #include "PathD2D.h" michael@0: #include "GradientStopsD2D.h" michael@0: #include "ScaledFontDWrite.h" michael@0: #include "ImageScaling.h" michael@0: #include "Logging.h" michael@0: #include "Tools.h" michael@0: #include michael@0: #include "mozilla/Constants.h" michael@0: #include "FilterNodeSoftware.h" michael@0: michael@0: #ifdef USE_D2D1_1 michael@0: #include "FilterNodeD2D1.h" michael@0: #endif michael@0: michael@0: #include michael@0: michael@0: // decltype is not usable for overloaded functions. michael@0: typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)( michael@0: D2D1_FACTORY_TYPE factoryType, michael@0: REFIID iid, michael@0: CONST D2D1_FACTORY_OPTIONS *pFactoryOptions, michael@0: void **factory michael@0: ); michael@0: michael@0: using namespace std; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: struct Vertex { michael@0: float x; michael@0: float y; michael@0: }; michael@0: michael@0: ID2D1Factory *DrawTargetD2D::mFactory; michael@0: IDWriteFactory *DrawTargetD2D::mDWriteFactory; michael@0: uint64_t DrawTargetD2D::mVRAMUsageDT; michael@0: uint64_t DrawTargetD2D::mVRAMUsageSS; michael@0: michael@0: // Helper class to restore surface contents that was clipped out but may have michael@0: // been altered by a drawing call. michael@0: class AutoSaveRestoreClippedOut michael@0: { michael@0: public: michael@0: AutoSaveRestoreClippedOut(DrawTargetD2D *aDT) michael@0: : mDT(aDT) michael@0: {} michael@0: michael@0: void Save() { michael@0: if (!mDT->mPushedClips.size()) { michael@0: return; michael@0: } michael@0: michael@0: mDT->Flush(); michael@0: michael@0: RefPtr tmpTexture; michael@0: IntSize size = mDT->mSize; michael@0: SurfaceFormat format = mDT->mFormat; michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, michael@0: 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture)); michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create temporary texture to hold surface data."; michael@0: } michael@0: mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture); michael@0: michael@0: D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format)); michael@0: michael@0: RefPtr surf; michael@0: michael@0: tmpTexture->QueryInterface((IDXGISurface**)byRef(surf)); michael@0: michael@0: hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf, michael@0: &props, byRef(mOldSurfBitmap)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create shared bitmap for old surface."; michael@0: } michael@0: michael@0: IntRect clipBounds; michael@0: mClippedArea = mDT->GetClippedGeometry(&clipBounds); michael@0: michael@0: if (!clipBounds.IsEqualEdges(IntRect(IntPoint(0, 0), mDT->mSize))) { michael@0: // We still need to take into account clipBounds if it contains additional michael@0: // clipping information. michael@0: RefPtr rectGeom; michael@0: factory()->CreateRectangleGeometry(D2D1::Rect(Float(clipBounds.x), michael@0: Float(clipBounds.y), michael@0: Float(clipBounds.XMost()), michael@0: Float(clipBounds.YMost())), michael@0: byRef(rectGeom)); michael@0: michael@0: mClippedArea = IntersectGeometry(mClippedArea, rectGeom); michael@0: } michael@0: } michael@0: michael@0: ID2D1Factory *factory() { return mDT->factory(); } michael@0: michael@0: ~AutoSaveRestoreClippedOut() michael@0: { michael@0: if (!mOldSurfBitmap) { michael@0: return; michael@0: } michael@0: michael@0: ID2D1RenderTarget *rt = mDT->mRT; michael@0: michael@0: // Write the area that was clipped out back to the surface. This all michael@0: // happens in device space. michael@0: rt->SetTransform(D2D1::IdentityMatrix()); michael@0: mDT->mTransformDirty = true; michael@0: michael@0: RefPtr rectGeom; michael@0: factory()->CreateRectangleGeometry( michael@0: D2D1::RectF(0, 0, float(mDT->mSize.width), float(mDT->mSize.height)), michael@0: byRef(rectGeom)); michael@0: michael@0: RefPtr invClippedArea; michael@0: factory()->CreatePathGeometry(byRef(invClippedArea)); michael@0: RefPtr sink; michael@0: invClippedArea->Open(byRef(sink)); michael@0: michael@0: rectGeom->CombineWithGeometry(mClippedArea, D2D1_COMBINE_MODE_EXCLUDE, nullptr, sink); michael@0: sink->Close(); michael@0: michael@0: RefPtr brush; michael@0: rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), byRef(brush)); michael@0: michael@0: rt->FillGeometry(invClippedArea, brush); michael@0: } michael@0: michael@0: private: michael@0: michael@0: DrawTargetD2D *mDT; michael@0: michael@0: // If we have an operator unbound by the source, this will contain a bitmap michael@0: // with the old dest surface data. michael@0: RefPtr mOldSurfBitmap; michael@0: // This contains the area drawing is clipped to. michael@0: RefPtr mClippedArea; michael@0: }; michael@0: michael@0: ID2D1Factory *D2DFactory() michael@0: { michael@0: return DrawTargetD2D::factory(); michael@0: } michael@0: michael@0: DrawTargetD2D::DrawTargetD2D() michael@0: : mCurrentCachedLayer(0) michael@0: , mClipsArePushed(false) michael@0: , mPrivateData(nullptr) michael@0: { michael@0: } michael@0: michael@0: DrawTargetD2D::~DrawTargetD2D() michael@0: { michael@0: if (mRT) { michael@0: PopAllClips(); michael@0: michael@0: mRT->EndDraw(); michael@0: michael@0: mVRAMUsageDT -= GetByteSize(); michael@0: } michael@0: if (mTempRT) { michael@0: mTempRT->EndDraw(); michael@0: michael@0: mVRAMUsageDT -= GetByteSize(); michael@0: } michael@0: michael@0: if (mSnapshot) { michael@0: // We may hold the only reference. MarkIndependent will clear mSnapshot; michael@0: // keep the snapshot object alive so it doesn't get destroyed while michael@0: // MarkIndependent is running. michael@0: RefPtr deathGrip = mSnapshot; michael@0: // mSnapshot can be treated as independent of this DrawTarget since we know michael@0: // this DrawTarget won't change again. michael@0: deathGrip->MarkIndependent(); michael@0: // mSnapshot will be cleared now. michael@0: } michael@0: michael@0: for (int i = 0; i < kLayerCacheSize; i++) { michael@0: if (mCachedLayers[i]) { michael@0: mCachedLayers[i] = nullptr; michael@0: mVRAMUsageDT -= GetByteSize(); michael@0: } michael@0: } michael@0: michael@0: // Targets depending on us can break that dependency, since we're obviously not going to michael@0: // be modified in the future. michael@0: for (TargetSet::iterator iter = mDependentTargets.begin(); michael@0: iter != mDependentTargets.end(); iter++) { michael@0: (*iter)->mDependingOnTargets.erase(this); michael@0: } michael@0: // Our dependencies on other targets no longer matter. michael@0: for (TargetSet::iterator iter = mDependingOnTargets.begin(); michael@0: iter != mDependingOnTargets.end(); iter++) { michael@0: (*iter)->mDependentTargets.erase(this); michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * DrawTarget Implementation michael@0: */ michael@0: TemporaryRef michael@0: DrawTargetD2D::Snapshot() michael@0: { michael@0: if (!mSnapshot) { michael@0: mSnapshot = new SourceSurfaceD2DTarget(this, mTexture, mFormat); michael@0: Flush(); michael@0: } michael@0: michael@0: return mSnapshot; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::Flush() michael@0: { michael@0: PopAllClips(); michael@0: michael@0: HRESULT hr = mRT->Flush(); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hr; michael@0: } michael@0: michael@0: // We no longer depend on any target. michael@0: for (TargetSet::iterator iter = mDependingOnTargets.begin(); michael@0: iter != mDependingOnTargets.end(); iter++) { michael@0: (*iter)->mDependentTargets.erase(this); michael@0: } michael@0: mDependingOnTargets.clear(); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::AddDependencyOnSource(SourceSurfaceD2DTarget* aSource) michael@0: { michael@0: if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) { michael@0: aSource->mDrawTarget->mDependentTargets.insert(this); michael@0: mDependingOnTargets.insert(aSource->mDrawTarget); michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface, michael@0: Rect &aSource) michael@0: { michael@0: RefPtr bitmap; michael@0: michael@0: switch (aSurface->GetType()) { michael@0: michael@0: case SurfaceType::D2D1_BITMAP: michael@0: { michael@0: SourceSurfaceD2D *srcSurf = static_cast(aSurface); michael@0: bitmap = srcSurf->GetBitmap(); michael@0: } michael@0: break; michael@0: case SurfaceType::D2D1_DRAWTARGET: michael@0: { michael@0: SourceSurfaceD2DTarget *srcSurf = static_cast(aSurface); michael@0: bitmap = srcSurf->GetBitmap(mRT); michael@0: AddDependencyOnSource(srcSurf); michael@0: } michael@0: break; michael@0: default: michael@0: { michael@0: RefPtr srcSurf = aSurface->GetDataSurface(); michael@0: michael@0: if (!srcSurf) { michael@0: gfxDebug() << "Not able to deal with non-data source surface."; michael@0: return nullptr; michael@0: } michael@0: michael@0: // We need to include any pixels that are overlapped by aSource michael@0: Rect sourceRect(aSource); michael@0: sourceRect.RoundOut(); michael@0: michael@0: if (sourceRect.IsEmpty()) { michael@0: gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail."; michael@0: return nullptr; michael@0: } michael@0: michael@0: if (sourceRect.width > mRT->GetMaximumBitmapSize() || michael@0: sourceRect.height > mRT->GetMaximumBitmapSize()) { michael@0: gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail."; michael@0: // Don't know how to deal with this yet. michael@0: return nullptr; michael@0: } michael@0: michael@0: int stride = srcSurf->Stride(); michael@0: michael@0: unsigned char *data = srcSurf->GetData() + michael@0: (uint32_t)sourceRect.y * stride + michael@0: (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat()); michael@0: michael@0: D2D1_BITMAP_PROPERTIES props = michael@0: D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat())); michael@0: mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap)); michael@0: michael@0: // subtract the integer part leaving the fractional part michael@0: aSource.x -= (uint32_t)aSource.x; michael@0: aSource.y -= (uint32_t)aSource.y; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: return bitmap; michael@0: } michael@0: michael@0: #ifdef USE_D2D1_1 michael@0: TemporaryRef michael@0: DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface) michael@0: { michael@0: RefPtr image; michael@0: michael@0: if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { michael@0: image = static_cast(aSurface)->GetImage(); michael@0: static_cast(aSurface)->EnsureIndependent(); michael@0: } else { michael@0: Rect r(Point(), Size(aSurface->GetSize())); michael@0: image = GetBitmapForSurface(aSurface, r); michael@0: } michael@0: michael@0: return image; michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: DrawTargetD2D::DrawSurface(SourceSurface *aSurface, michael@0: const Rect &aDest, michael@0: const Rect &aSource, michael@0: const DrawSurfaceOptions &aSurfOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: RefPtr bitmap; michael@0: michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: Rect srcRect = aSource; michael@0: michael@0: bitmap = GetBitmapForSurface(aSurface, srcRect); michael@0: if (!bitmap) { michael@0: return; michael@0: } michael@0: michael@0: rt->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(srcRect)); michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), aDest); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::DrawFilter(FilterNode *aNode, michael@0: const Rect &aSourceRect, michael@0: const Point &aDestPoint, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: #ifdef USE_D2D1_1 michael@0: RefPtr dc; michael@0: HRESULT hr; michael@0: michael@0: hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc)); michael@0: michael@0: if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) { michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: hr = rt->QueryInterface((ID2D1DeviceContext**)byRef(dc)); michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: dc->DrawImage(static_cast(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); michael@0: michael@0: Rect destRect = aSourceRect; michael@0: destRect.MoveBy(aDestPoint); michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect); michael@0: return; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) { michael@0: gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!"; michael@0: return; michael@0: } michael@0: michael@0: FilterNodeSoftware* filter = static_cast(aNode); michael@0: filter->Draw(this, aSourceRect, aDestPoint, aOptions); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::MaskSurface(const Pattern &aSource, michael@0: SourceSurface *aMask, michael@0: Point aOffset, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: RefPtr bitmap; michael@0: michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: // FillOpacityMask only works if the antialias mode is MODE_ALIASED michael@0: rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); michael@0: michael@0: IntSize size = aMask->GetSize(); michael@0: Rect maskRect = Rect(0.f, 0.f, size.width, size.height); michael@0: bitmap = GetBitmapForSurface(aMask, maskRect); michael@0: if (!bitmap) { michael@0: return; michael@0: } michael@0: michael@0: Rect dest = Rect(aOffset.x, aOffset.y, size.width, size.height); michael@0: RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); michael@0: rt->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), dest); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface, michael@0: const Point &aDest, michael@0: const Color &aColor, michael@0: const Point &aOffset, michael@0: Float aSigma, michael@0: CompositionOp aOperator) michael@0: { michael@0: RefPtr srView = nullptr; michael@0: if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) { michael@0: return; michael@0: } michael@0: michael@0: SetScissorToRect(nullptr); michael@0: michael@0: // XXX - This function is way too long, it should be split up soon to make michael@0: // it more graspable! michael@0: michael@0: Flush(); michael@0: michael@0: AutoSaveRestoreClippedOut restoreClippedOut(this); michael@0: michael@0: if (!IsOperatorBoundByMask(aOperator)) { michael@0: restoreClippedOut.Save(); michael@0: } michael@0: michael@0: srView = static_cast(aSurface)->GetSRView(); michael@0: michael@0: EnsureViews(); michael@0: michael@0: if (!mTempRTView) { michael@0: // This view is only needed in this path. michael@0: HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, byRef(mTempRTView)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failure to create RenderTargetView. Code: " << hr; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: michael@0: RefPtr destRTView = mRTView; michael@0: RefPtr destTexture; michael@0: HRESULT hr; michael@0: michael@0: RefPtr maskTexture; michael@0: RefPtr maskSRView; michael@0: IntRect clipBounds; michael@0: if (mPushedClips.size()) { michael@0: EnsureClipMaskTexture(&clipBounds); michael@0: michael@0: mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(maskSRView)); michael@0: } michael@0: michael@0: IntSize srcSurfSize; michael@0: ID3D10RenderTargetView *rtViews; michael@0: D3D10_VIEWPORT viewport; michael@0: michael@0: UINT stride = sizeof(Vertex); michael@0: UINT offset = 0; michael@0: ID3D10Buffer *buff = mPrivateData->mVB; michael@0: michael@0: mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); michael@0: mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); michael@0: mDevice->IASetInputLayout(mPrivateData->mInputLayout); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); michael@0: mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); michael@0: michael@0: // If we create a downsampled source surface we need to correct aOffset for that. michael@0: Point correctedOffset = aOffset + aDest; michael@0: michael@0: // The 'practical' scaling factors. michael@0: Float dsFactorX = 1.0f; michael@0: Float dsFactorY = 1.0f; michael@0: michael@0: if (aSigma > 1.7f) { michael@0: // In this case 9 samples of our original will not cover it. Generate the michael@0: // mip levels for the original and create a downsampled version from michael@0: // them. We generate a version downsampled so that a kernel for a sigma michael@0: // of 1.7 will produce the right results. michael@0: float blurWeights[9] = { 0.234671f, 0.197389f, 0.197389f, 0.117465f, 0.117465f, 0.049456f, 0.049456f, 0.014732f, 0.014732f }; michael@0: mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, michael@0: aSurface->GetSize().width, michael@0: aSurface->GetSize().height); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS; michael@0: michael@0: RefPtr mipTexture; michael@0: hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mipTexture)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failure to create temporary texture. Size: " << michael@0: aSurface->GetSize() << " Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: IntSize dsSize = IntSize(int32_t(aSurface->GetSize().width * (1.7f / aSigma)), michael@0: int32_t(aSurface->GetSize().height * (1.7f / aSigma))); michael@0: michael@0: if (dsSize.width < 1) { michael@0: dsSize.width = 1; michael@0: } michael@0: if (dsSize.height < 1) { michael@0: dsSize.height = 1; michael@0: } michael@0: michael@0: dsFactorX = dsSize.width / Float(aSurface->GetSize().width); michael@0: dsFactorY = dsSize.height / Float(aSurface->GetSize().height); michael@0: correctedOffset.x *= dsFactorX; michael@0: correctedOffset.y *= dsFactorY; michael@0: michael@0: desc = CD3D10_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM, michael@0: dsSize.width, michael@0: dsSize.height, 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: RefPtr tmpDSTexture; michael@0: hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpDSTexture)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failure to create temporary texture. Size: " << dsSize << " Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: D3D10_BOX box; michael@0: box.left = box.top = box.front = 0; michael@0: box.back = 1; michael@0: box.right = aSurface->GetSize().width; michael@0: box.bottom = aSurface->GetSize().height; michael@0: mDevice->CopySubresourceRegion(mipTexture, 0, 0, 0, 0, static_cast(aSurface)->mTexture, 0, &box); michael@0: michael@0: mDevice->CreateShaderResourceView(mipTexture, nullptr, byRef(srView)); michael@0: mDevice->GenerateMips(srView); michael@0: michael@0: RefPtr dsRTView; michael@0: RefPtr dsSRView; michael@0: mDevice->CreateRenderTargetView(tmpDSTexture, nullptr, byRef(dsRTView)); michael@0: mDevice->CreateShaderResourceView(tmpDSTexture, nullptr, byRef(dsSRView)); michael@0: michael@0: // We're not guaranteed the texture we created will be empty, we've michael@0: // seen old content at least on NVidia drivers. michael@0: float color[4] = { 0, 0, 0, 0 }; michael@0: mDevice->ClearRenderTargetView(dsRTView, color); michael@0: michael@0: rtViews = dsRTView; michael@0: mDevice->OMSetRenderTargets(1, &rtViews, nullptr); michael@0: michael@0: viewport.MaxDepth = 1; michael@0: viewport.MinDepth = 0; michael@0: viewport.Height = dsSize.height; michael@0: viewport.Width = dsSize.width; michael@0: viewport.TopLeftX = 0; michael@0: viewport.TopLeftY = 0; michael@0: michael@0: mDevice->RSSetViewports(1, &viewport); michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: michael@0: mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff); michael@0: michael@0: mDevice->Draw(4, 0); michael@0: michael@0: srcSurfSize = dsSize; michael@0: michael@0: srView = dsSRView; michael@0: } else { michael@0: // In this case generate a kernel to draw the blur directly to the temp michael@0: // surf in one direction and to final in the other. michael@0: float blurWeights[9]; michael@0: michael@0: float normalizeFactor = 1.0f; michael@0: if (aSigma != 0) { michael@0: normalizeFactor = 1.0f / Float(sqrt(2 * M_PI * pow(aSigma, 2))); michael@0: } michael@0: michael@0: blurWeights[0] = normalizeFactor; michael@0: michael@0: // XXX - We should actually optimize for Sigma = 0 here. We could use a michael@0: // much simpler shader and save a lot of texture lookups. michael@0: for (int i = 1; i < 9; i += 2) { michael@0: if (aSigma != 0) { michael@0: blurWeights[i] = blurWeights[i + 1] = normalizeFactor * michael@0: exp(-pow(float((i + 1) / 2), 2) / (2 * pow(aSigma, 2))); michael@0: } else { michael@0: blurWeights[i] = blurWeights[i + 1] = 0; michael@0: } michael@0: } michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights)); michael@0: michael@0: viewport.MaxDepth = 1; michael@0: viewport.MinDepth = 0; michael@0: viewport.Height = aSurface->GetSize().height; michael@0: viewport.Width = aSurface->GetSize().width; michael@0: viewport.TopLeftX = 0; michael@0: viewport.TopLeftY = 0; michael@0: michael@0: mDevice->RSSetViewports(1, &viewport); michael@0: michael@0: srcSurfSize = aSurface->GetSize(); michael@0: } michael@0: michael@0: // We may need to draw to a different intermediate surface if our temp michael@0: // texture isn't big enough. michael@0: bool needBiggerTemp = srcSurfSize.width > mSize.width || michael@0: srcSurfSize.height > mSize.height; michael@0: michael@0: RefPtr tmpRTView; michael@0: RefPtr tmpSRView; michael@0: RefPtr tmpTexture; michael@0: michael@0: IntSize tmpSurfSize = mSize; michael@0: michael@0: if (!needBiggerTemp) { michael@0: tmpRTView = mTempRTView; michael@0: tmpSRView = mSRView; michael@0: michael@0: // There could still be content here! michael@0: float color[4] = { 0, 0, 0, 0 }; michael@0: mDevice->ClearRenderTargetView(tmpRTView, color); michael@0: } else { michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, michael@0: srcSurfSize.width, michael@0: srcSurfSize.height, michael@0: 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture)); michael@0: mDevice->CreateRenderTargetView(tmpTexture, nullptr, byRef(tmpRTView)); michael@0: mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(tmpSRView)); michael@0: michael@0: tmpSurfSize = srcSurfSize; michael@0: } michael@0: michael@0: rtViews = tmpRTView; michael@0: mDevice->OMSetRenderTargets(1, &rtViews, nullptr); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); michael@0: michael@0: // Premultiplied! michael@0: float shadowColor[4] = { aColor.r * aColor.a, aColor.g * aColor.a, michael@0: aColor.b * aColor.a, aColor.a }; michael@0: mPrivateData->mEffect->GetVariableByName("ShadowColor")->AsVector()-> michael@0: SetFloatVector(shadowColor); michael@0: michael@0: float pixelOffset = 1.0f / float(srcSurfSize.width); michael@0: float blurOffsetsH[9] = { 0, pixelOffset, -pixelOffset, michael@0: 2.0f * pixelOffset, -2.0f * pixelOffset, michael@0: 3.0f * pixelOffset, -3.0f * pixelOffset, michael@0: 4.0f * pixelOffset, - 4.0f * pixelOffset }; michael@0: michael@0: pixelOffset = 1.0f / float(tmpSurfSize.height); michael@0: float blurOffsetsV[9] = { 0, pixelOffset, -pixelOffset, michael@0: 2.0f * pixelOffset, -2.0f * pixelOffset, michael@0: 3.0f * pixelOffset, -3.0f * pixelOffset, michael@0: 4.0f * pixelOffset, - 4.0f * pixelOffset }; michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("BlurOffsetsH")-> michael@0: SetRawValue(blurOffsetsH, 0, sizeof(blurOffsetsH)); michael@0: mPrivateData->mEffect->GetVariableByName("BlurOffsetsV")-> michael@0: SetRawValue(blurOffsetsV, 0, sizeof(blurOffsetsV)); michael@0: michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: michael@0: mDevice->Draw(4, 0); michael@0: michael@0: viewport.MaxDepth = 1; michael@0: viewport.MinDepth = 0; michael@0: viewport.Height = mSize.height; michael@0: viewport.Width = mSize.width; michael@0: viewport.TopLeftX = 0; michael@0: viewport.TopLeftY = 0; michael@0: michael@0: mDevice->RSSetViewports(1, &viewport); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(tmpSRView); michael@0: michael@0: rtViews = destRTView; michael@0: mDevice->OMSetRenderTargets(1, &rtViews, nullptr); michael@0: michael@0: Point shadowDest = aDest + aOffset; michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((shadowDest.x / mSize.width) * 2.0f), michael@0: 1.0f - (shadowDest.y / mSize.height * 2.0f), michael@0: (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, michael@0: (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); michael@0: mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, Float(srcSurfSize.width) / tmpSurfSize.width, michael@0: Float(srcSurfSize.height) / tmpSurfSize.height)); michael@0: michael@0: if (mPushedClips.size()) { michael@0: mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView); michael@0: mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height, michael@0: Float(aSurface->GetSize().width) / mSize.width, michael@0: Float(aSurface->GetSize().height) / mSize.height)); michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> michael@0: GetPassByIndex(2)->Apply(0); michael@0: SetScissorToRect(&clipBounds); michael@0: } else { michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")-> michael@0: GetPassByIndex(1)->Apply(0); michael@0: } michael@0: michael@0: mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); michael@0: michael@0: mDevice->Draw(4, 0); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((aDest.x / mSize.width) * 2.0f), michael@0: 1.0f - (aDest.y / mSize.height * 2.0f), michael@0: (Float(aSurface->GetSize().width) / mSize.width) * 2.0f, michael@0: (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f)); michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(static_cast(aSurface)->GetSRView()); michael@0: mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); michael@0: michael@0: if (mPushedClips.size()) { michael@0: mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height, michael@0: Float(aSurface->GetSize().width) / mSize.width, michael@0: Float(aSurface->GetSize().height) / mSize.height)); michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: // We've set the scissor rect here for the previous draw call. michael@0: } else { michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTexture")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: } michael@0: michael@0: mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); michael@0: michael@0: mDevice->Draw(4, 0); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::ClearRect(const Rect &aRect) michael@0: { michael@0: MarkChanged(); michael@0: PushClipRect(aRect); michael@0: michael@0: PopAllClips(); michael@0: michael@0: AutoSaveRestoreClippedOut restoreClippedOut(this); michael@0: michael@0: D2D1_RECT_F clipRect; michael@0: bool isPixelAligned; michael@0: bool pushedClip = false; michael@0: if (mTransform.IsRectilinear() && michael@0: GetDeviceSpaceClipRect(clipRect, isPixelAligned)) { michael@0: if (mTransformDirty || michael@0: !mTransform.IsIdentity()) { michael@0: mRT->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: } michael@0: michael@0: mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); michael@0: pushedClip = true; michael@0: } else { michael@0: FlushTransformToRT(); michael@0: restoreClippedOut.Save(); michael@0: } michael@0: michael@0: mRT->Clear(D2D1::ColorF(0, 0.0f)); michael@0: michael@0: if (pushedClip) { michael@0: mRT->PopAxisAlignedClip(); michael@0: } michael@0: michael@0: PopClip(); michael@0: return; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::CopySurface(SourceSurface *aSurface, michael@0: const IntRect &aSourceRect, michael@0: const IntPoint &aDestination) michael@0: { michael@0: MarkChanged(); michael@0: michael@0: Rect srcRect(Float(aSourceRect.x), Float(aSourceRect.y), michael@0: Float(aSourceRect.width), Float(aSourceRect.height)); michael@0: Rect dstRect(Float(aDestination.x), Float(aDestination.y), michael@0: Float(aSourceRect.width), Float(aSourceRect.height)); michael@0: michael@0: mRT->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: mRT->PushAxisAlignedClip(D2DRect(dstRect), D2D1_ANTIALIAS_MODE_ALIASED); michael@0: mRT->Clear(D2D1::ColorF(0, 0.0f)); michael@0: mRT->PopAxisAlignedClip(); michael@0: michael@0: RefPtr bitmap = GetBitmapForSurface(aSurface, srcRect); michael@0: if (!bitmap) { michael@0: return; michael@0: } michael@0: michael@0: if (aSurface->GetFormat() == SurfaceFormat::A8) { michael@0: RefPtr brush; michael@0: mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), michael@0: D2D1::BrushProperties(), byRef(brush)); michael@0: mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); michael@0: mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS); michael@0: } else { michael@0: mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f, michael@0: D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, michael@0: D2DRect(srcRect)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::FillRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: if (brush) { michael@0: rt->FillRectangle(D2DRect(aRect), brush); michael@0: } michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::StrokeRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: if (brush && strokeStyle) { michael@0: rt->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: } michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::StrokeLine(const Point &aStart, michael@0: const Point &aEnd, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: if (brush && strokeStyle) { michael@0: rt->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: } michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::Stroke(const Path *aPath, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: if (aPath->GetBackendType() != BackendType::DIRECT2D) { michael@0: gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; michael@0: return; michael@0: } michael@0: michael@0: const PathD2D *d2dPath = static_cast(aPath); michael@0: michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: if (brush && strokeStyle) { michael@0: rt->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: } michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height))); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::Fill(const Path *aPath, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: if (aPath->GetBackendType() != BackendType::DIRECT2D) { michael@0: gfxDebug() << *this << ": Ignoring drawing call for incompatible path."; michael@0: return; michael@0: } michael@0: michael@0: const PathD2D *d2dPath = static_cast(aPath); michael@0: michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode)); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: if (brush) { michael@0: rt->FillGeometry(d2dPath->mGeometry, brush); michael@0: } michael@0: michael@0: Rect bounds; michael@0: if (aOptions.mCompositionOp != CompositionOp::OP_OVER) { michael@0: D2D1_RECT_F d2dbounds; michael@0: d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds); michael@0: bounds = ToRect(d2dbounds); michael@0: } michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, bounds); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::FillGlyphs(ScaledFont *aFont, michael@0: const GlyphBuffer &aBuffer, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions, michael@0: const GlyphRenderingOptions* aRenderOptions) michael@0: { michael@0: if (aFont->GetType() != FontType::DWRITE) { michael@0: gfxDebug() << *this << ": Ignoring drawing call for incompatible font."; michael@0: return; michael@0: } michael@0: michael@0: ScaledFontDWrite *font = static_cast(aFont); michael@0: michael@0: IDWriteRenderingParams *params = nullptr; michael@0: if (aRenderOptions) { michael@0: if (aRenderOptions->GetType() != FontType::DWRITE) { michael@0: gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions."; michael@0: // This should never happen. michael@0: MOZ_ASSERT(false); michael@0: } else { michael@0: params = static_cast(aRenderOptions)->mParams; michael@0: } michael@0: } michael@0: michael@0: AntialiasMode aaMode = font->GetDefaultAAMode(); michael@0: michael@0: if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) { michael@0: aaMode = aOptions.mAntialiasMode; michael@0: } michael@0: michael@0: if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && michael@0: aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR && michael@0: aaMode == AntialiasMode::SUBPIXEL) { michael@0: if (FillGlyphsManual(font, aBuffer, michael@0: static_cast(&aPattern)->mColor, michael@0: params, aOptions)) { michael@0: return; michael@0: } michael@0: } michael@0: michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; michael@0: michael@0: switch (aaMode) { michael@0: case AntialiasMode::NONE: michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED; michael@0: break; michael@0: case AntialiasMode::GRAY: michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; michael@0: break; michael@0: case AntialiasMode::SUBPIXEL: michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE; michael@0: break; michael@0: default: michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT; michael@0: } michael@0: michael@0: if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE && michael@0: mFormat != SurfaceFormat::B8G8R8X8) { michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; michael@0: } michael@0: michael@0: rt->SetTextAntialiasMode(d2dAAMode); michael@0: michael@0: if (rt != mRT || params != mTextRenderingParams) { michael@0: rt->SetTextRenderingParams(params); michael@0: if (rt == mRT) { michael@0: mTextRenderingParams = params; michael@0: } michael@0: } michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: AutoDWriteGlyphRun autoRun; michael@0: DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun); michael@0: michael@0: if (brush) { michael@0: rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); michael@0: } michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::Mask(const Pattern &aSource, michael@0: const Pattern &aMask, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aSource); michael@0: michael@0: PrepareForDrawing(rt); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); michael@0: RefPtr maskBrush = CreateBrushForPattern(aMask, 1.0f); michael@0: michael@0: RefPtr layer; michael@0: michael@0: layer = GetCachedLayer(); michael@0: michael@0: rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, michael@0: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, michael@0: D2D1::IdentityMatrix(), michael@0: 1.0f, maskBrush), michael@0: layer); michael@0: michael@0: Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height); michael@0: Matrix mat = mTransform; michael@0: mat.Invert(); michael@0: michael@0: rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush); michael@0: PopCachedLayer(rt); michael@0: michael@0: FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PushClip(const Path *aPath) michael@0: { michael@0: if (aPath->GetBackendType() != BackendType::DIRECT2D) { michael@0: gfxDebug() << *this << ": Ignoring clipping call for incompatible path."; michael@0: return; michael@0: } michael@0: michael@0: mCurrentClipMaskTexture = nullptr; michael@0: mCurrentClippedGeometry = nullptr; michael@0: michael@0: RefPtr pathD2D = static_cast(const_cast(aPath)); michael@0: michael@0: PushedClip clip; michael@0: clip.mTransform = D2DMatrix(mTransform); michael@0: clip.mPath = pathD2D; michael@0: michael@0: pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds); michael@0: michael@0: clip.mLayer = GetCachedLayer(); michael@0: michael@0: mPushedClips.push_back(clip); michael@0: michael@0: // The transform of clips is relative to the world matrix, since we use the total michael@0: // transform for the clips, make the world matrix identity. michael@0: mRT->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: if (mClipsArePushed) { michael@0: PushD2DLayer(mRT, pathD2D->mGeometry, clip.mLayer, clip.mTransform); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PushClipRect(const Rect &aRect) michael@0: { michael@0: mCurrentClipMaskTexture = nullptr; michael@0: mCurrentClippedGeometry = nullptr; michael@0: if (!mTransform.IsRectilinear()) { michael@0: // Whoops, this isn't a rectangle in device space, Direct2D will not deal michael@0: // with this transform the way we want it to. michael@0: // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx michael@0: michael@0: RefPtr pathBuilder = CreatePathBuilder(); michael@0: pathBuilder->MoveTo(aRect.TopLeft()); michael@0: pathBuilder->LineTo(aRect.TopRight()); michael@0: pathBuilder->LineTo(aRect.BottomRight()); michael@0: pathBuilder->LineTo(aRect.BottomLeft()); michael@0: pathBuilder->Close(); michael@0: RefPtr path = pathBuilder->Finish(); michael@0: return PushClip(path); michael@0: } michael@0: michael@0: PushedClip clip; michael@0: Rect rect = mTransform.TransformBounds(aRect); michael@0: IntRect intRect; michael@0: clip.mIsPixelAligned = rect.ToIntRect(&intRect); michael@0: michael@0: // Do not store the transform, just store the device space rectangle directly. michael@0: clip.mBounds = D2DRect(rect); michael@0: michael@0: mPushedClips.push_back(clip); michael@0: michael@0: mRT->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: if (mClipsArePushed) { michael@0: mRT->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PopClip() michael@0: { michael@0: mCurrentClipMaskTexture = nullptr; michael@0: mCurrentClippedGeometry = nullptr; michael@0: if (mClipsArePushed) { michael@0: if (mPushedClips.back().mLayer) { michael@0: PopCachedLayer(mRT); michael@0: } else { michael@0: mRT->PopAxisAlignedClip(); michael@0: } michael@0: } michael@0: mPushedClips.pop_back(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData, michael@0: const IntSize &aSize, michael@0: int32_t aStride, michael@0: SurfaceFormat aFormat) const michael@0: { michael@0: RefPtr newSurf = new SourceSurfaceD2D(); michael@0: michael@0: if (!newSurf->InitFromData(aData, aSize, aStride, aFormat, mRT)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return newSurf; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const michael@0: { michael@0: if (aSurface->GetType() == SurfaceType::D2D1_BITMAP || michael@0: aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) { michael@0: return aSurface; michael@0: } michael@0: michael@0: RefPtr data = aSurface->GetDataSurface(); michael@0: michael@0: DataSourceSurface::MappedSurface map; michael@0: if (!data->Map(DataSourceSurface::MapType::READ, &map)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr newSurf = new SourceSurfaceD2D(); michael@0: bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT); michael@0: michael@0: data->Unmap(); michael@0: michael@0: if (!success) { michael@0: return data; michael@0: } michael@0: return newSurf; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const michael@0: { michael@0: if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) { michael@0: gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface."; michael@0: return nullptr; michael@0: } michael@0: RefPtr newSurf = new SourceSurfaceD2D(); michael@0: michael@0: if (!newSurf->InitFromTexture(static_cast(aSurface.mSurface), michael@0: aSurface.mFormat, michael@0: mRT)) michael@0: { michael@0: gfxWarning() << *this << ": Failed to create SourceSurface from texture."; michael@0: return nullptr; michael@0: } michael@0: michael@0: return newSurf; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const michael@0: { michael@0: RefPtr newTarget = michael@0: new DrawTargetD2D(); michael@0: michael@0: if (!newTarget->Init(aSize, aFormat)) { michael@0: gfxDebug() << *this << ": Failed to create optimal draw target. Size: " << aSize; michael@0: return nullptr; michael@0: } michael@0: michael@0: return newTarget; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const michael@0: { michael@0: RefPtr path; michael@0: HRESULT hr = factory()->CreatePathGeometry(byRef(path)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hr; michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr sink; michael@0: hr = path->Open(byRef(sink)); michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hr; michael@0: return nullptr; michael@0: } michael@0: michael@0: if (aFillRule == FillRule::FILL_WINDING) { michael@0: sink->SetFillMode(D2D1_FILL_MODE_WINDING); michael@0: } michael@0: michael@0: return new PathBuilderD2D(sink, path, aFillRule); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const michael@0: { michael@0: D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops]; michael@0: michael@0: for (uint32_t i = 0; i < aNumStops; i++) { michael@0: stops[i].position = rawStops[i].offset; michael@0: stops[i].color = D2DColor(rawStops[i].color); michael@0: } michael@0: michael@0: RefPtr stopCollection; michael@0: michael@0: HRESULT hr = michael@0: mRT->CreateGradientStopCollection(stops, aNumStops, michael@0: D2D1_GAMMA_2_2, D2DExtend(aExtendMode), michael@0: byRef(stopCollection)); michael@0: delete [] stops; michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create GradientStopCollection. Code: " << hr; michael@0: return nullptr; michael@0: } michael@0: michael@0: return new GradientStopsD2D(stopCollection); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateFilter(FilterType aType) michael@0: { michael@0: #ifdef USE_D2D1_1 michael@0: RefPtr dc; michael@0: HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc)); michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: return FilterNodeD2D1::Create(this, dc, aType); michael@0: } michael@0: #endif michael@0: return FilterNodeSoftware::Create(aType); michael@0: } michael@0: michael@0: void* michael@0: DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType) michael@0: { michael@0: if (aType != NativeSurfaceType::D3D10_TEXTURE) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return mTexture; michael@0: } michael@0: michael@0: /* michael@0: * Public functions michael@0: */ michael@0: bool michael@0: DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: mSize = aSize; michael@0: mFormat = aFormat; michael@0: michael@0: if (!Factory::GetDirect3D10Device()) { michael@0: gfxDebug() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)"; michael@0: return false; michael@0: } michael@0: mDevice = Factory::GetDirect3D10Device(); michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGIFormat(aFormat), michael@0: mSize.width, michael@0: mSize.height, michael@0: 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxDebug() << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hr; michael@0: return false; michael@0: } michael@0: michael@0: if (!InitD2DRenderTarget()) { michael@0: return false; michael@0: } michael@0: michael@0: mRT->Clear(D2D1::ColorF(0, 0)); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: mTexture = aTexture; michael@0: mFormat = aFormat; michael@0: michael@0: if (!mTexture) { michael@0: gfxDebug() << "No valid texture for Direct2D draw target initialization."; michael@0: return false; michael@0: } michael@0: michael@0: RefPtr device; michael@0: mTexture->GetDevice(byRef(device)); michael@0: michael@0: hr = device->QueryInterface((ID3D10Device1**)byRef(mDevice)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to get D3D10 device from texture."; michael@0: return false; michael@0: } michael@0: michael@0: D3D10_TEXTURE2D_DESC desc; michael@0: mTexture->GetDesc(&desc); michael@0: mSize.width = desc.Width; michael@0: mSize.height = desc.Height; michael@0: michael@0: return InitD2DRenderTarget(); michael@0: } michael@0: michael@0: // {0D398B49-AE7B-416F-B26D-EA3C137D1CF7} michael@0: static const GUID sPrivateDataD2D = michael@0: { 0xd398b49, 0xae7b, 0x416f, { 0xb2, 0x6d, 0xea, 0x3c, 0x13, 0x7d, 0x1c, 0xf7 } }; michael@0: michael@0: bool michael@0: DrawTargetD2D::InitD3D10Data() michael@0: { michael@0: HRESULT hr; michael@0: michael@0: UINT privateDataSize; michael@0: privateDataSize = sizeof(mPrivateData); michael@0: hr = mDevice->GetPrivateData(sPrivateDataD2D, &privateDataSize, &mPrivateData); michael@0: michael@0: if (SUCCEEDED(hr)) { michael@0: return true; michael@0: } michael@0: michael@0: mPrivateData = new PrivateD3D10DataD2D; michael@0: michael@0: decltype(D3D10CreateEffectFromMemory)* createD3DEffect; michael@0: HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll"); michael@0: createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*) michael@0: GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory"); michael@0: michael@0: hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, byRef(mPrivateData->mEffect)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hr; michael@0: return false; michael@0: } michael@0: michael@0: privateDataSize = sizeof(mPrivateData); michael@0: mDevice->SetPrivateData(sPrivateDataD2D, privateDataSize, &mPrivateData); michael@0: michael@0: D3D10_INPUT_ELEMENT_DESC layout[] = michael@0: { michael@0: { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 }, michael@0: }; michael@0: D3D10_PASS_DESC passDesc; michael@0: michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->GetDesc(&passDesc); michael@0: michael@0: hr = mDevice->CreateInputLayout(layout, michael@0: sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC), michael@0: passDesc.pIAInputSignature, michael@0: passDesc.IAInputSignatureSize, michael@0: byRef(mPrivateData->mInputLayout)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hr; michael@0: return false; michael@0: } michael@0: michael@0: D3D10_SUBRESOURCE_DATA data; michael@0: Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} }; michael@0: data.pSysMem = vertices; michael@0: CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); michael@0: michael@0: hr = mDevice->CreateBuffer(&bufferDesc, &data, byRef(mPrivateData->mVB)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hr; michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /* michael@0: * Private helpers michael@0: */ michael@0: uint32_t michael@0: DrawTargetD2D::GetByteSize() const michael@0: { michael@0: return mSize.width * mSize.height * BytesPerPixel(mFormat); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::GetCachedLayer() michael@0: { michael@0: RefPtr layer; michael@0: michael@0: if (mCurrentCachedLayer < 5) { michael@0: if (!mCachedLayers[mCurrentCachedLayer]) { michael@0: mRT->CreateLayer(byRef(mCachedLayers[mCurrentCachedLayer])); michael@0: mVRAMUsageDT += GetByteSize(); michael@0: } michael@0: layer = mCachedLayers[mCurrentCachedLayer]; michael@0: } else { michael@0: mRT->CreateLayer(byRef(layer)); michael@0: } michael@0: michael@0: mCurrentCachedLayer++; michael@0: return layer; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PopCachedLayer(ID2D1RenderTarget *aRT) michael@0: { michael@0: aRT->PopLayer(); michael@0: mCurrentCachedLayer--; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetD2D::InitD2DRenderTarget() michael@0: { michael@0: if (!factory()) { michael@0: return false; michael@0: } michael@0: michael@0: mRT = CreateRTForTexture(mTexture, mFormat); michael@0: michael@0: if (!mRT) { michael@0: return false; michael@0: } michael@0: michael@0: mRT->BeginDraw(); michael@0: michael@0: if (mFormat == SurfaceFormat::B8G8R8X8) { michael@0: mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); michael@0: } michael@0: michael@0: mVRAMUsageDT += GetByteSize(); michael@0: michael@0: return InitD3D10Data(); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT) michael@0: { michael@0: if (!mClipsArePushed || aRT == mTempRT) { michael@0: if (mPushedClips.size()) { michael@0: // The transform of clips is relative to the world matrix, since we use the total michael@0: // transform for the clips, make the world matrix identity. michael@0: aRT->SetTransform(D2D1::IdentityMatrix()); michael@0: if (aRT == mRT) { michael@0: mTransformDirty = true; michael@0: mClipsArePushed = true; michael@0: } michael@0: PushClipsToRT(aRT); michael@0: } michael@0: } michael@0: FlushTransformToRT(); michael@0: MarkChanged(); michael@0: michael@0: if (aRT == mTempRT) { michael@0: mTempRT->SetTransform(D2DMatrix(mTransform)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::MarkChanged() michael@0: { michael@0: if (mSnapshot) { michael@0: if (mSnapshot->hasOneRef()) { michael@0: // Just destroy it, since no-one else knows about it. michael@0: mSnapshot = nullptr; michael@0: } else { michael@0: mSnapshot->DrawTargetWillChange(); michael@0: // The snapshot will no longer depend on this target. michael@0: MOZ_ASSERT(!mSnapshot); michael@0: } michael@0: } michael@0: if (mDependentTargets.size()) { michael@0: // Copy mDependentTargets since the Flush()es below will modify it. michael@0: TargetSet tmpTargets = mDependentTargets; michael@0: for (TargetSet::iterator iter = tmpTargets.begin(); michael@0: iter != tmpTargets.end(); iter++) { michael@0: (*iter)->Flush(); michael@0: } michael@0: // The Flush() should have broken all dependencies on this target. michael@0: MOZ_ASSERT(!mDependentTargets.size()); michael@0: } michael@0: } michael@0: michael@0: ID3D10BlendState* michael@0: DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator) michael@0: { michael@0: size_t operatorIndex = static_cast(aOperator); michael@0: if (mPrivateData->mBlendStates[operatorIndex]) { michael@0: return mPrivateData->mBlendStates[operatorIndex]; michael@0: } michael@0: michael@0: D3D10_BLEND_DESC desc; michael@0: michael@0: memset(&desc, 0, sizeof(D3D10_BLEND_DESC)); michael@0: michael@0: desc.AlphaToCoverageEnable = FALSE; michael@0: desc.BlendEnable[0] = TRUE; michael@0: desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL; michael@0: desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD; michael@0: michael@0: switch (aOperator) { michael@0: case CompositionOp::OP_ADD: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; michael@0: break; michael@0: case CompositionOp::OP_IN: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; michael@0: break; michael@0: case CompositionOp::OP_OUT: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; michael@0: break; michael@0: case CompositionOp::OP_ATOP: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; michael@0: break; michael@0: case CompositionOp::OP_DEST_IN: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; michael@0: break; michael@0: case CompositionOp::OP_DEST_OUT: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; michael@0: break; michael@0: case CompositionOp::OP_DEST_ATOP: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA; michael@0: break; michael@0: case CompositionOp::OP_DEST_OVER: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE; michael@0: break; michael@0: case CompositionOp::OP_XOR: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; michael@0: break; michael@0: case CompositionOp::OP_SOURCE: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO; michael@0: break; michael@0: default: michael@0: desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE; michael@0: desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA; michael@0: } michael@0: michael@0: mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[operatorIndex])); michael@0: michael@0: return mPrivateData->mBlendStates[operatorIndex]; michael@0: } michael@0: michael@0: /* This function prepares the temporary RT for drawing and returns it when a michael@0: * drawing operation other than OVER is required. michael@0: */ michael@0: ID2D1RenderTarget* michael@0: DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern) michael@0: { michael@0: if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { michael@0: return mRT; michael@0: } michael@0: michael@0: PopAllClips(); michael@0: michael@0: if (aOperator > CompositionOp::OP_XOR) { michael@0: mRT->Flush(); michael@0: } michael@0: michael@0: if (mTempRT) { michael@0: mTempRT->Clear(D2D1::ColorF(0, 0)); michael@0: return mTempRT; michael@0: } michael@0: michael@0: EnsureViews(); michael@0: michael@0: if (!mRTView || !mSRView) { michael@0: gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER."; michael@0: return mRT; michael@0: } michael@0: michael@0: mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8); michael@0: michael@0: if (!mTempRT) { michael@0: return mRT; michael@0: } michael@0: michael@0: mVRAMUsageDT += GetByteSize(); michael@0: michael@0: mTempRT->BeginDraw(); michael@0: michael@0: mTempRT->Clear(D2D1::ColorF(0, 0)); michael@0: michael@0: return mTempRT; michael@0: } michael@0: michael@0: /* This function blends back the content of a drawing operation (drawn to an michael@0: * empty surface with OVER, so the surface now contains the source operation michael@0: * contents) to the rendertarget using the requested composition operation. michael@0: * In order to respect clip for operations which are unbound by their mask, michael@0: * the old content of the surface outside the clipped area may be blended back michael@0: * to the surface. michael@0: */ michael@0: void michael@0: DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds) michael@0: { michael@0: if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { michael@0: return; michael@0: } michael@0: michael@0: if (!mTempRT) { michael@0: return; michael@0: } michael@0: michael@0: PopClipsFromRT(mTempRT); michael@0: michael@0: mRT->Flush(); michael@0: mTempRT->Flush(); michael@0: michael@0: AutoSaveRestoreClippedOut restoreClippedOut(this); michael@0: michael@0: bool needsWriteBack = michael@0: !IsOperatorBoundByMask(aOperator) && mPushedClips.size(); michael@0: michael@0: if (needsWriteBack) { michael@0: restoreClippedOut.Save(); michael@0: } michael@0: michael@0: ID3D10RenderTargetView *rtViews = mRTView; michael@0: mDevice->OMSetRenderTargets(1, &rtViews, nullptr); michael@0: michael@0: UINT stride = sizeof(Vertex); michael@0: UINT offset = 0; michael@0: ID3D10Buffer *buff = mPrivateData->mVB; michael@0: michael@0: mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); michael@0: mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); michael@0: mDevice->IASetInputLayout(mPrivateData->mInputLayout); michael@0: michael@0: D3D10_VIEWPORT viewport; michael@0: viewport.MaxDepth = 1; michael@0: viewport.MinDepth = 0; michael@0: viewport.Height = mSize.height; michael@0: viewport.Width = mSize.width; michael@0: viewport.TopLeftX = 0; michael@0: viewport.TopLeftY = 0; michael@0: michael@0: RefPtr tmpTexture; michael@0: RefPtr mBckSRView; michael@0: michael@0: mDevice->RSSetViewports(1, &viewport); michael@0: mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f)); michael@0: michael@0: if (IsPatternSupportedByD2D(aPattern)) { michael@0: mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView); michael@0: michael@0: // Handle the case where we blend with the backdrop michael@0: if (aOperator > CompositionOp::OP_XOR) { michael@0: IntSize size = mSize; michael@0: SurfaceFormat format = mFormat; michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture)); michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create temporary texture to hold surface data."; michael@0: return; michael@0: } michael@0: michael@0: mDevice->CopyResource(tmpTexture, mTexture); michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: DrawTargetD2D::Flush(); michael@0: michael@0: hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(mBckSRView)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR; michael@0: mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView); michael@0: mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop); michael@0: michael@0: if (aOperator > CompositionOp::OP_EXCLUSION) michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: else if (aOperator > CompositionOp::OP_COLOR_DODGE) michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: else michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_1")-> michael@0: GetPassByIndex(0)->Apply(0); michael@0: } michael@0: else { michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0); michael@0: } michael@0: michael@0: } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { michael@0: const RadialGradientPattern *pat = static_cast(&aPattern); michael@0: michael@0: if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) { michael@0: // Draw nothing! michael@0: return; michael@0: } michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(mSRView); michael@0: michael@0: SetupEffectForRadialGradient(pat); michael@0: } michael@0: michael@0: mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff); michael@0: michael@0: SetScissorToRect(nullptr); michael@0: mDevice->Draw(4, 0); michael@0: } michael@0: michael@0: static D2D1_RECT_F michael@0: IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2) michael@0: { michael@0: D2D1_RECT_F result; michael@0: result.left = max(aRect1.left, aRect2.left); michael@0: result.top = max(aRect1.top, aRect2.top); michael@0: result.right = min(aRect1.right, aRect2.right); michael@0: result.bottom = min(aRect1.bottom, aRect2.bottom); michael@0: michael@0: result.right = max(result.right, result.left); michael@0: result.bottom = max(result.bottom, result.top); michael@0: michael@0: return result; michael@0: } michael@0: michael@0: bool michael@0: DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned) michael@0: { michael@0: if (!mPushedClips.size()) { michael@0: return false; michael@0: } michael@0: michael@0: std::vector::iterator iter = mPushedClips.begin(); michael@0: if (iter->mPath) { michael@0: return false; michael@0: } michael@0: aClipRect = iter->mBounds; michael@0: aIsPixelAligned = iter->mIsPixelAligned; michael@0: michael@0: iter++; michael@0: for (;iter != mPushedClips.end(); iter++) { michael@0: if (iter->mPath) { michael@0: return false; michael@0: } michael@0: aClipRect = IntersectRect(aClipRect, iter->mBounds); michael@0: if (!iter->mIsPixelAligned) { michael@0: aIsPixelAligned = false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds) michael@0: { michael@0: if (mCurrentClippedGeometry) { michael@0: *aClipBounds = mCurrentClipBounds; michael@0: return mCurrentClippedGeometry; michael@0: } michael@0: michael@0: mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize); michael@0: michael@0: // if pathGeom is null then pathRect represents the path. michael@0: RefPtr pathGeom; michael@0: D2D1_RECT_F pathRect; michael@0: bool pathRectIsAxisAligned = false; michael@0: std::vector::iterator iter = mPushedClips.begin(); michael@0: michael@0: if (iter->mPath) { michael@0: pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); michael@0: } else { michael@0: pathRect = iter->mBounds; michael@0: pathRectIsAxisAligned = iter->mIsPixelAligned; michael@0: } michael@0: michael@0: iter++; michael@0: for (;iter != mPushedClips.end(); iter++) { michael@0: // Do nothing but add it to the current clip bounds. michael@0: if (!iter->mPath && iter->mIsPixelAligned) { michael@0: mCurrentClipBounds.IntersectRect(mCurrentClipBounds, michael@0: IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top), michael@0: int32_t(iter->mBounds.right - iter->mBounds.left), michael@0: int32_t(iter->mBounds.bottom - iter->mBounds.top))); michael@0: continue; michael@0: } michael@0: michael@0: if (!pathGeom) { michael@0: if (pathRectIsAxisAligned) { michael@0: mCurrentClipBounds.IntersectRect(mCurrentClipBounds, michael@0: IntRect(int32_t(pathRect.left), int32_t(pathRect.top), michael@0: int32_t(pathRect.right - pathRect.left), michael@0: int32_t(pathRect.bottom - pathRect.top))); michael@0: } michael@0: if (iter->mPath) { michael@0: // See if pathRect needs to go into the path geometry. michael@0: if (!pathRectIsAxisAligned) { michael@0: pathGeom = ConvertRectToGeometry(pathRect); michael@0: } else { michael@0: pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform); michael@0: } michael@0: } else { michael@0: pathRect = IntersectRect(pathRect, iter->mBounds); michael@0: pathRectIsAxisAligned = false; michael@0: continue; michael@0: } michael@0: } michael@0: michael@0: RefPtr newGeom; michael@0: factory()->CreatePathGeometry(byRef(newGeom)); michael@0: michael@0: RefPtr currentSink; michael@0: newGeom->Open(byRef(currentSink)); michael@0: michael@0: if (iter->mPath) { michael@0: pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT, michael@0: iter->mTransform, currentSink); michael@0: } else { michael@0: RefPtr rectGeom = ConvertRectToGeometry(iter->mBounds); michael@0: pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT, michael@0: D2D1::IdentityMatrix(), currentSink); michael@0: } michael@0: michael@0: currentSink->Close(); michael@0: michael@0: pathGeom = newGeom.forget(); michael@0: } michael@0: michael@0: // For now we need mCurrentClippedGeometry to always be non-nullptr. This michael@0: // method might seem a little strange but it is just fine, if pathGeom is michael@0: // nullptr pathRect will always still contain 1 clip unaccounted for michael@0: // regardless of mCurrentClipBounds. michael@0: if (!pathGeom) { michael@0: pathGeom = ConvertRectToGeometry(pathRect); michael@0: } michael@0: mCurrentClippedGeometry = pathGeom.forget(); michael@0: *aClipBounds = mCurrentClipBounds; michael@0: return mCurrentClippedGeometry; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: RefPtr surface; michael@0: RefPtr rt; michael@0: michael@0: hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to QI texture to surface."; michael@0: return nullptr; michael@0: } michael@0: michael@0: D3D10_TEXTURE2D_DESC desc; michael@0: aTexture->GetDesc(&desc); michael@0: michael@0: D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; michael@0: michael@0: if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) { michael@0: alphaMode = D2D1_ALPHA_MODE_IGNORE; michael@0: } michael@0: michael@0: D2D1_RENDER_TARGET_PROPERTIES props = michael@0: D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode)); michael@0: hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create D2D render target for texture."; michael@0: return nullptr; michael@0: } michael@0: michael@0: return rt; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::EnsureViews() michael@0: { michael@0: if (mTempTexture && mSRView && mRTView) { michael@0: return; michael@0: } michael@0: michael@0: HRESULT hr; michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, michael@0: mSize.width, michael@0: mSize.height, michael@0: 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTempTexture)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: " michael@0: << mSize << " Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, byRef(mSRView)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr; michael@0: return; michael@0: } michael@0: michael@0: hr = mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(mRTView)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PopAllClips() michael@0: { michael@0: if (mClipsArePushed) { michael@0: PopClipsFromRT(mRT); michael@0: michael@0: mClipsArePushed = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PushClipsToRT(ID2D1RenderTarget *aRT) michael@0: { michael@0: for (std::vector::iterator iter = mPushedClips.begin(); michael@0: iter != mPushedClips.end(); iter++) { michael@0: if (iter->mLayer) { michael@0: PushD2DLayer(aRT, iter->mPath->mGeometry, iter->mLayer, iter->mTransform); michael@0: } else { michael@0: aRT->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT) michael@0: { michael@0: for (int i = mPushedClips.size() - 1; i >= 0; i--) { michael@0: if (mPushedClips[i].mLayer) { michael@0: aRT->PopLayer(); michael@0: } else { michael@0: aRT->PopAxisAlignedClip(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds) michael@0: { michael@0: if (mCurrentClipMaskTexture || mPushedClips.empty()) { michael@0: *aBounds = mCurrentClipBounds; michael@0: return; michael@0: } michael@0: michael@0: RefPtr geometry = GetClippedGeometry(aBounds); michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM, michael@0: mSize.width, michael@0: mSize.height, michael@0: 1, 1); michael@0: desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; michael@0: michael@0: HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mCurrentClipMaskTexture)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create texture for ClipMask!"; michael@0: return; michael@0: } michael@0: michael@0: RefPtr rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8); michael@0: michael@0: if (!rt) { michael@0: gfxWarning() << "Failed to create RT for ClipMask!"; michael@0: return; michael@0: } michael@0: michael@0: RefPtr brush; michael@0: rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush)); michael@0: michael@0: rt->BeginDraw(); michael@0: rt->Clear(D2D1::ColorF(0, 0)); michael@0: rt->FillGeometry(geometry, brush); michael@0: rt->EndDraw(); michael@0: } michael@0: michael@0: bool michael@0: DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont, michael@0: const GlyphBuffer &aBuffer, michael@0: const Color &aColor, michael@0: IDWriteRenderingParams *aParams, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: RefPtr params; michael@0: michael@0: if (aParams) { michael@0: params = aParams; michael@0: } else { michael@0: mRT->GetTextRenderingParams(byRef(params)); michael@0: } michael@0: michael@0: DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT; michael@0: if (params) { michael@0: hr = aFont->mFontFace->GetRecommendedRenderingMode( michael@0: (FLOAT)aFont->GetSize(), michael@0: 1.0f, michael@0: DWRITE_MEASURING_MODE_NATURAL, michael@0: params, michael@0: &renderMode); michael@0: if (FAILED(hr)) { michael@0: // this probably never happens, but let's play it safe michael@0: renderMode = DWRITE_RENDERING_MODE_DEFAULT; michael@0: } michael@0: } michael@0: michael@0: // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. michael@0: switch (renderMode) { michael@0: case DWRITE_RENDERING_MODE_ALIASED: michael@0: // ClearType texture creation will fail in this mode, so bail out michael@0: return false; michael@0: case DWRITE_RENDERING_MODE_DEFAULT: michael@0: // As per DWRITE_RENDERING_MODE documentation, pick Natural for font michael@0: // sizes under 16 ppem michael@0: if (aFont->GetSize() < 16.0f) { michael@0: renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL; michael@0: } else { michael@0: renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; michael@0: } michael@0: break; michael@0: case DWRITE_RENDERING_MODE_OUTLINE: michael@0: renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; michael@0: break; michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: DWRITE_MEASURING_MODE measureMode = michael@0: renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC : michael@0: renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL : michael@0: DWRITE_MEASURING_MODE_NATURAL; michael@0: michael@0: DWRITE_MATRIX mat = DWriteMatrixFromMatrix(mTransform); michael@0: michael@0: AutoDWriteGlyphRun autoRun; michael@0: DWriteGlyphRunFromGlyphs(aBuffer, aFont, &autoRun); michael@0: michael@0: RefPtr analysis; michael@0: hr = GetDWriteFactory()->CreateGlyphRunAnalysis(&autoRun, 1.0f, &mat, michael@0: renderMode, measureMode, 0, 0, byRef(analysis)); michael@0: michael@0: if (FAILED(hr)) { michael@0: return false; michael@0: } michael@0: michael@0: RECT bounds; michael@0: hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds); michael@0: michael@0: if (bounds.bottom <= bounds.top || bounds.right <= bounds.left) { michael@0: // DWrite seems to do this sometimes. I'm not 100% sure why. See bug 758980. michael@0: gfxDebug() << "Empty alpha texture bounds! Falling back to regular drawing."; michael@0: return false; michael@0: } michael@0: IntRect rectBounds(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top); michael@0: IntRect surfBounds(IntPoint(0, 0), mSize); michael@0: michael@0: rectBounds.IntersectRect(rectBounds, surfBounds); michael@0: michael@0: if (rectBounds.IsEmpty()) { michael@0: // Nothing to do. michael@0: return true; michael@0: } michael@0: michael@0: RefPtr tex = CreateTextureForAnalysis(analysis, rectBounds); michael@0: michael@0: if (!tex) { michael@0: return false; michael@0: } michael@0: michael@0: RefPtr srView; michael@0: hr = mDevice->CreateShaderResourceView(tex, nullptr, byRef(srView)); michael@0: michael@0: if (FAILED(hr)) { michael@0: return false; michael@0: } michael@0: michael@0: MarkChanged(); michael@0: michael@0: // Prepare our background texture for drawing. michael@0: PopAllClips(); michael@0: mRT->Flush(); michael@0: michael@0: SetupStateForRendering(); michael@0: michael@0: ID3D10EffectTechnique *technique = mPrivateData->mEffect->GetTechniqueByName("SampleTextTexture"); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((Float(rectBounds.x) / mSize.width) * 2.0f), michael@0: 1.0f - (Float(rectBounds.y) / mSize.height * 2.0f), michael@0: (Float(rectBounds.width) / mSize.width) * 2.0f, michael@0: (-Float(rectBounds.height) / mSize.height) * 2.0f)); michael@0: mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); michael@0: FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a }; michael@0: mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()-> michael@0: SetFloatVector(color); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); michael@0: michael@0: bool isMasking = false; michael@0: michael@0: IntRect clipBoundsStorage; michael@0: IntRect *clipBounds = nullptr; michael@0: michael@0: if (!mPushedClips.empty()) { michael@0: clipBounds = &clipBoundsStorage; michael@0: RefPtr geom = GetClippedGeometry(clipBounds); michael@0: michael@0: RefPtr rectGeom; michael@0: factory()->CreateRectangleGeometry(D2D1::RectF(Float(rectBounds.x), michael@0: Float(rectBounds.y), michael@0: Float(rectBounds.width + rectBounds.x), michael@0: Float(rectBounds.height + rectBounds.y)), michael@0: byRef(rectGeom)); michael@0: michael@0: D2D1_GEOMETRY_RELATION relation; michael@0: if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) || michael@0: relation != D2D1_GEOMETRY_RELATION_CONTAINS ) { michael@0: isMasking = true; michael@0: } michael@0: } michael@0: michael@0: if (isMasking) { michael@0: clipBounds = &clipBoundsStorage; michael@0: EnsureClipMaskTexture(clipBounds); michael@0: michael@0: RefPtr srViewMask; michael@0: hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(srViewMask)); michael@0: michael@0: if (FAILED(hr)) { michael@0: return false; michael@0: } michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(srViewMask); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(Float(rectBounds.x) / mSize.width, Float(rectBounds.y) / mSize.height, michael@0: Float(rectBounds.width) / mSize.width, Float(rectBounds.height) / mSize.height)); michael@0: michael@0: technique->GetPassByIndex(1)->Apply(0); michael@0: } else { michael@0: technique->GetPassByIndex(0)->Apply(0); michael@0: } michael@0: michael@0: RefPtr rtView; michael@0: ID3D10RenderTargetView *rtViews; michael@0: mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(rtView)); michael@0: michael@0: rtViews = rtView; michael@0: mDevice->OMSetRenderTargets(1, &rtViews, nullptr); michael@0: SetScissorToRect(clipBounds); michael@0: mDevice->Draw(4, 0); michael@0: return true; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) michael@0: { michael@0: if (!IsPatternSupportedByD2D(aPattern)) { michael@0: RefPtr colBrush; michael@0: mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush)); michael@0: return colBrush; michael@0: } michael@0: michael@0: if (aPattern.GetType() == PatternType::COLOR) { michael@0: RefPtr colBrush; michael@0: Color color = static_cast(&aPattern)->mColor; michael@0: mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g, michael@0: color.b, color.a), michael@0: D2D1::BrushProperties(aAlpha), michael@0: byRef(colBrush)); michael@0: return colBrush; michael@0: } else if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) { michael@0: RefPtr gradBrush; michael@0: const LinearGradientPattern *pat = michael@0: static_cast(&aPattern); michael@0: michael@0: GradientStopsD2D *stops = static_cast(pat->mStops.get()); michael@0: michael@0: if (!stops) { michael@0: gfxDebug() << "No stops specified for gradient pattern."; michael@0: return nullptr; michael@0: } michael@0: michael@0: if (pat->mBegin == pat->mEnd) { michael@0: RefPtr colBrush; michael@0: uint32_t stopCount = stops->mStopCollection->GetGradientStopCount(); michael@0: vector d2dStops(stopCount); michael@0: stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount); michael@0: mRT->CreateSolidColorBrush(d2dStops.back().color, michael@0: D2D1::BrushProperties(aAlpha), michael@0: byRef(colBrush)); michael@0: return colBrush; michael@0: } michael@0: michael@0: mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin), michael@0: D2DPoint(pat->mEnd)), michael@0: D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), michael@0: stops->mStopCollection, michael@0: byRef(gradBrush)); michael@0: return gradBrush; michael@0: } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) { michael@0: RefPtr gradBrush; michael@0: const RadialGradientPattern *pat = michael@0: static_cast(&aPattern); michael@0: michael@0: GradientStopsD2D *stops = static_cast(pat->mStops.get()); michael@0: michael@0: if (!stops) { michael@0: gfxDebug() << "No stops specified for gradient pattern."; michael@0: return nullptr; michael@0: } michael@0: michael@0: // This will not be a complex radial gradient brush. michael@0: mRT->CreateRadialGradientBrush( michael@0: D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2), michael@0: D2DPoint(pat->mCenter1 - pat->mCenter2), michael@0: pat->mRadius2, pat->mRadius2), michael@0: D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)), michael@0: stops->mStopCollection, michael@0: byRef(gradBrush)); michael@0: michael@0: return gradBrush; michael@0: } else if (aPattern.GetType() == PatternType::SURFACE) { michael@0: RefPtr bmBrush; michael@0: const SurfacePattern *pat = michael@0: static_cast(&aPattern); michael@0: michael@0: if (!pat->mSurface) { michael@0: gfxDebug() << "No source surface specified for surface pattern"; michael@0: return nullptr; michael@0: } michael@0: michael@0: RefPtr bitmap; michael@0: michael@0: Matrix mat = pat->mMatrix; michael@0: michael@0: switch (pat->mSurface->GetType()) { michael@0: case SurfaceType::D2D1_BITMAP: michael@0: { michael@0: SourceSurfaceD2D *surf = static_cast(pat->mSurface.get()); michael@0: michael@0: bitmap = surf->mBitmap; michael@0: michael@0: if (!bitmap) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: break; michael@0: case SurfaceType::D2D1_DRAWTARGET: michael@0: { michael@0: SourceSurfaceD2DTarget *surf = michael@0: static_cast(pat->mSurface.get()); michael@0: bitmap = surf->GetBitmap(mRT); michael@0: AddDependencyOnSource(surf); michael@0: } michael@0: break; michael@0: default: michael@0: { michael@0: RefPtr dataSurf = pat->mSurface->GetDataSurface(); michael@0: if (!dataSurf) { michael@0: gfxWarning() << "Invalid surface type."; michael@0: return nullptr; michael@0: } michael@0: michael@0: bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT); michael@0: if (!bitmap) { michael@0: return nullptr; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: michael@0: mRT->CreateBitmapBrush(bitmap, michael@0: D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode), michael@0: D2DExtend(pat->mExtendMode), michael@0: D2DFilter(pat->mFilter)), michael@0: D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), michael@0: byRef(bmBrush)); michael@0: michael@0: return bmBrush; michael@0: } michael@0: michael@0: gfxWarning() << "Invalid pattern type detected."; michael@0: return nullptr; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops) michael@0: { michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 4096, 1, 1, 1); michael@0: michael@0: std::vector rawStops; michael@0: rawStops.resize(aStops->mStopCollection->GetGradientStopCount()); michael@0: aStops->mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size()); michael@0: michael@0: std::vector textureData; michael@0: textureData.resize(4096 * 4); michael@0: unsigned char *texData = &textureData.front(); michael@0: michael@0: float prevColorPos = 0; michael@0: float nextColorPos = 1.0f; michael@0: D2D1_COLOR_F prevColor = rawStops[0].color; michael@0: D2D1_COLOR_F nextColor = prevColor; michael@0: michael@0: if (rawStops.size() >= 2) { michael@0: nextColor = rawStops[1].color; michael@0: nextColorPos = rawStops[1].position; michael@0: } michael@0: michael@0: uint32_t stopPosition = 2; michael@0: michael@0: // Not the most optimized way but this will do for now. michael@0: for (int i = 0; i < 4096; i++) { michael@0: // The 4095 seems a little counter intuitive, but we want the gradient michael@0: // color at offset 0 at the first pixel, and at offset 1.0f at the last michael@0: // pixel. michael@0: float pos = float(i) / 4095; michael@0: michael@0: while (pos > nextColorPos) { michael@0: prevColor = nextColor; michael@0: prevColorPos = nextColorPos; michael@0: if (rawStops.size() > stopPosition) { michael@0: nextColor = rawStops[stopPosition].color; michael@0: nextColorPos = rawStops[stopPosition++].position; michael@0: } else { michael@0: nextColorPos = 1.0f; michael@0: } michael@0: } michael@0: michael@0: float interp; michael@0: michael@0: if (nextColorPos != prevColorPos) { michael@0: interp = (pos - prevColorPos) / (nextColorPos - prevColorPos); michael@0: } else { michael@0: interp = 0; michael@0: } michael@0: michael@0: Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp, michael@0: prevColor.g + (nextColor.g - prevColor.g) * interp, michael@0: prevColor.b + (nextColor.b - prevColor.b) * interp, michael@0: prevColor.a + (nextColor.a - prevColor.a) * interp); michael@0: michael@0: texData[i * 4] = (char)(255.0f * newColor.b); michael@0: texData[i * 4 + 1] = (char)(255.0f * newColor.g); michael@0: texData[i * 4 + 2] = (char)(255.0f * newColor.r); michael@0: texData[i * 4 + 3] = (char)(255.0f * newColor.a); michael@0: } michael@0: michael@0: D3D10_SUBRESOURCE_DATA data; michael@0: data.pSysMem = &textureData.front(); michael@0: data.SysMemPitch = 4096 * 4; michael@0: michael@0: RefPtr tex; michael@0: mDevice->CreateTexture2D(&desc, &data, byRef(tex)); michael@0: michael@0: return tex; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: uint32_t bufferSize = aBounds.width * aBounds.height * 3; michael@0: michael@0: RECT bounds; michael@0: bounds.left = aBounds.x; michael@0: bounds.top = aBounds.y; michael@0: bounds.right = aBounds.x + aBounds.width; michael@0: bounds.bottom = aBounds.y + aBounds.height; michael@0: michael@0: // Add one byte so we can safely read a 32-bit int when copying the last michael@0: // 3 bytes. michael@0: BYTE *texture = new BYTE[bufferSize + 1]; michael@0: hr = aAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize); michael@0: michael@0: if (FAILED(hr)) { michael@0: delete [] texture; michael@0: return nullptr; michael@0: } michael@0: michael@0: int alignedBufferSize = aBounds.width * aBounds.height * 4; michael@0: michael@0: // Create a one-off immutable texture from system memory. michael@0: BYTE *alignedTextureData = new BYTE[alignedBufferSize]; michael@0: for (int y = 0; y < aBounds.height; y++) { michael@0: for (int x = 0; x < aBounds.width; x++) { michael@0: // Copy 3 Bpp source to 4 Bpp destination memory used for michael@0: // texture creation. D3D10 has no 3 Bpp texture format we can michael@0: // use. michael@0: // michael@0: // Since we don't care what ends up in the alpha pixel of the michael@0: // destination, therefor we can simply copy a normal 32 bit michael@0: // integer each time, filling the alpha pixel of the destination michael@0: // with the first subpixel of the next pixel from the source. michael@0: *((int*)(alignedTextureData + (y * aBounds.width + x) * 4)) = michael@0: *((int*)(texture + (y * aBounds.width + x) * 3)); michael@0: } michael@0: } michael@0: michael@0: D3D10_SUBRESOURCE_DATA data; michael@0: michael@0: CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, michael@0: aBounds.width, aBounds.height, michael@0: 1, 1); michael@0: desc.Usage = D3D10_USAGE_IMMUTABLE; michael@0: michael@0: data.SysMemPitch = aBounds.width * 4; michael@0: data.pSysMem = alignedTextureData; michael@0: michael@0: RefPtr tex; michael@0: hr = mDevice->CreateTexture2D(&desc, &data, byRef(tex)); michael@0: michael@0: delete [] alignedTextureData; michael@0: delete [] texture; michael@0: michael@0: if (FAILED(hr)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return tex; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern) michael@0: { michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0); michael@0: mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()-> michael@0: SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f)); michael@0: michael@0: float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 }; michael@0: mPrivateData->mEffect->GetVariableByName("dimensions")->AsVector()-> michael@0: SetFloatVector(dimensions); michael@0: michael@0: const GradientStopsD2D *stops = michael@0: static_cast(aPattern->mStops.get()); michael@0: michael@0: RefPtr tex = CreateGradientTexture(stops); michael@0: michael@0: RefPtr srView; michael@0: mDevice->CreateShaderResourceView(tex, nullptr, byRef(srView)); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView); michael@0: michael@0: Point dc = aPattern->mCenter2 - aPattern->mCenter1; michael@0: float dr = aPattern->mRadius2 - aPattern->mRadius1; michael@0: michael@0: float diffv[] = { dc.x, dc.y, dr, 0 }; michael@0: mPrivateData->mEffect->GetVariableByName("diff")->AsVector()-> michael@0: SetFloatVector(diffv); michael@0: michael@0: float center1[] = { aPattern->mCenter1.x, aPattern->mCenter1.y, dr, 0 }; michael@0: mPrivateData->mEffect->GetVariableByName("center1")->AsVector()-> michael@0: SetFloatVector(center1); michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("radius1")->AsScalar()-> michael@0: SetFloat(aPattern->mRadius1); michael@0: mPrivateData->mEffect->GetVariableByName("sq_radius1")->AsScalar()-> michael@0: SetFloat(pow(aPattern->mRadius1, 2)); michael@0: michael@0: Matrix invTransform = mTransform; michael@0: michael@0: if (!invTransform.Invert()) { michael@0: // Bail if the matrix is singular. michael@0: return; michael@0: } michael@0: float matrix[] = { invTransform._11, invTransform._12, 0, 0, michael@0: invTransform._21, invTransform._22, 0, 0, michael@0: invTransform._31, invTransform._32, 1.0f, 0, michael@0: 0, 0, 0, 1.0f }; michael@0: michael@0: mPrivateData->mEffect->GetVariableByName("DeviceSpaceToUserSpace")-> michael@0: AsMatrix()->SetMatrix(matrix); michael@0: michael@0: float A = dc.x * dc.x + dc.y * dc.y - dr * dr; michael@0: michael@0: uint32_t offset = 0; michael@0: switch (stops->mStopCollection->GetExtendMode()) { michael@0: case D2D1_EXTEND_MODE_WRAP: michael@0: offset = 1; michael@0: break; michael@0: case D2D1_EXTEND_MODE_MIRROR: michael@0: offset = 2; michael@0: break; michael@0: default: michael@0: gfxWarning() << "This shouldn't happen! Invalid extend mode for gradient stops."; michael@0: } michael@0: michael@0: if (A == 0) { michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> michael@0: GetPassByIndex(offset * 2 + 1)->Apply(0); michael@0: } else { michael@0: mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A); michael@0: mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")-> michael@0: GetPassByIndex(offset * 2)->Apply(0); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::SetupStateForRendering() michael@0: { michael@0: UINT stride = sizeof(Vertex); michael@0: UINT offset = 0; michael@0: ID3D10Buffer *buff = mPrivateData->mVB; michael@0: michael@0: mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP); michael@0: mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset); michael@0: mDevice->IASetInputLayout(mPrivateData->mInputLayout); michael@0: michael@0: D3D10_VIEWPORT viewport; michael@0: viewport.MaxDepth = 1; michael@0: viewport.MinDepth = 0; michael@0: viewport.Height = mSize.height; michael@0: viewport.Width = mSize.width; michael@0: viewport.TopLeftX = 0; michael@0: viewport.TopLeftY = 0; michael@0: michael@0: mDevice->RSSetViewports(1, &viewport); michael@0: } michael@0: michael@0: ID2D1Factory* michael@0: DrawTargetD2D::factory() michael@0: { michael@0: if (mFactory) { michael@0: return mFactory; michael@0: } michael@0: michael@0: D2D1CreateFactoryFunc createD2DFactory; michael@0: HMODULE d2dModule = LoadLibraryW(L"d2d1.dll"); michael@0: createD2DFactory = (D2D1CreateFactoryFunc) michael@0: GetProcAddress(d2dModule, "D2D1CreateFactory"); michael@0: michael@0: if (!createD2DFactory) { michael@0: gfxWarning() << "Failed to locate D2D1CreateFactory function."; michael@0: return nullptr; michael@0: } michael@0: michael@0: D2D1_FACTORY_OPTIONS options; michael@0: #ifdef _DEBUG michael@0: options.debugLevel = D2D1_DEBUG_LEVEL_WARNING; michael@0: #else michael@0: options.debugLevel = D2D1_DEBUG_LEVEL_NONE; michael@0: #endif michael@0: michael@0: HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, michael@0: __uuidof(ID2D1Factory), michael@0: &options, michael@0: (void**)&mFactory); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create Direct2D factory."; michael@0: } michael@0: michael@0: return mFactory; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::CleanupD2D() michael@0: { michael@0: if (mFactory) { michael@0: mFactory->Release(); michael@0: mFactory = nullptr; michael@0: } michael@0: } michael@0: michael@0: IDWriteFactory* michael@0: DrawTargetD2D::GetDWriteFactory() michael@0: { michael@0: if (mDWriteFactory) { michael@0: return mDWriteFactory; michael@0: } michael@0: michael@0: decltype(DWriteCreateFactory)* createDWriteFactory; michael@0: HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll"); michael@0: createDWriteFactory = (decltype(DWriteCreateFactory)*) michael@0: GetProcAddress(dwriteModule, "DWriteCreateFactory"); michael@0: michael@0: if (!createDWriteFactory) { michael@0: gfxWarning() << "Failed to locate DWriteCreateFactory function."; michael@0: return nullptr; michael@0: } michael@0: michael@0: HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory), michael@0: reinterpret_cast(&mDWriteFactory)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << "Failed to create DWrite Factory."; michael@0: } michael@0: michael@0: return mDWriteFactory; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::SetScissorToRect(IntRect *aRect) michael@0: { michael@0: D3D10_RECT rect; michael@0: if (aRect) { michael@0: rect.left = aRect->x; michael@0: rect.right = aRect->XMost(); michael@0: rect.top = aRect->y; michael@0: rect.bottom = aRect->YMost(); michael@0: } else { michael@0: rect.left = rect.top = INT32_MIN; michael@0: rect.right = rect.bottom = INT32_MAX; michael@0: } michael@0: michael@0: mDevice->RSSetScissorRects(1, &rect); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D::PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform) michael@0: { michael@0: D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; michael@0: D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE; michael@0: michael@0: if (aRT->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { michael@0: options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; michael@0: options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; michael@0: } michael@0: michael@0: RefPtr dc; michael@0: HRESULT hr = aRT->QueryInterface(IID_ID2D1DeviceContext, (void**)((ID2D1DeviceContext**)byRef(dc))); michael@0: michael@0: if (FAILED(hr)) { michael@0: aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), aGeometry, michael@0: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, michael@0: 1.0, nullptr, options), michael@0: aLayer); michael@0: } else { michael@0: dc->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, michael@0: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, michael@0: 1.0, nullptr, options1), michael@0: aLayer); michael@0: } michael@0: } michael@0: michael@0: } michael@0: }