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 "DrawTargetD2D1.h" michael@0: #include "DrawTargetD2D.h" michael@0: #include "FilterNodeSoftware.h" michael@0: #include "GradientStopsD2D.h" michael@0: #include "SourceSurfaceD2D1.h" michael@0: #include "SourceSurfaceD2D.h" michael@0: #include "RadialGradientEffectD2D1.h" michael@0: michael@0: #include "HelpersD2D.h" michael@0: #include "FilterNodeD2D1.h" michael@0: #include "Tools.h" michael@0: michael@0: using namespace std; michael@0: michael@0: namespace mozilla { michael@0: namespace gfx { michael@0: michael@0: uint64_t DrawTargetD2D1::mVRAMUsageDT; michael@0: uint64_t DrawTargetD2D1::mVRAMUsageSS; michael@0: ID2D1Factory1* DrawTargetD2D1::mFactory = nullptr; michael@0: michael@0: ID2D1Factory1 *D2DFactory1() michael@0: { michael@0: return DrawTargetD2D1::factory(); michael@0: } michael@0: michael@0: DrawTargetD2D1::DrawTargetD2D1() michael@0: : mClipsArePushed(false) michael@0: { michael@0: } michael@0: michael@0: DrawTargetD2D1::~DrawTargetD2D1() michael@0: { michael@0: PopAllClips(); michael@0: michael@0: mDC->EndDraw(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::Snapshot() michael@0: { michael@0: if (mSnapshot) { michael@0: return mSnapshot; michael@0: } michael@0: PopAllClips(); michael@0: michael@0: mDC->Flush(); michael@0: michael@0: mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this); michael@0: michael@0: return mSnapshot; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::Flush() michael@0: { michael@0: mDC->Flush(); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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 image = GetImageForSurface(aSurface, ExtendMode::CLAMP); michael@0: michael@0: if (!image) { michael@0: gfxWarning() << *this << ": Unable to get D2D image for surface."; michael@0: return; michael@0: } michael@0: michael@0: PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: michael@0: D2D1_RECT_F samplingBounds; michael@0: michael@0: if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) { michael@0: samplingBounds = D2DRect(aSource); michael@0: } else { michael@0: samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width), Float(aSurface->GetSize().height)); michael@0: } michael@0: michael@0: Float xScale = aDest.width / aSource.width; michael@0: Float yScale = aDest.height / aSource.height; michael@0: michael@0: RefPtr brush; michael@0: michael@0: // Here we scale the source pattern up to the size and position where we want michael@0: // it to be. michael@0: Matrix transform; michael@0: transform.Translate(aDest.x, aDest.y); michael@0: transform.Scale(xScale, yScale); michael@0: michael@0: mDC->CreateImageBrush(image, D2D1::ImageBrushProperties(samplingBounds), michael@0: D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)), michael@0: byRef(brush)); michael@0: mDC->FillRectangle(D2DRect(aDest), brush); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::DrawFilter(FilterNode *aNode, michael@0: const Rect &aSourceRect, michael@0: const Point &aDestPoint, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) { michael@0: gfxWarning() << *this << ": Incompatible filter passed to DrawFilter."; michael@0: return; michael@0: } michael@0: michael@0: PrepareForDrawing(aOptions.mCompositionOp, ColorPattern(Color())); michael@0: michael@0: mDC->DrawImage(static_cast(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: MarkChanged(); michael@0: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: Matrix mat; michael@0: RefPtr image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); michael@0: michael@0: if (!mat.IsIdentity()) { michael@0: gfxDebug() << *this << ": At this point complex partial uploads are not supported for Shadow surfaces."; michael@0: return; michael@0: } michael@0: michael@0: // Step 1, create the shadow effect. michael@0: RefPtr shadowEffect; michael@0: mDC->CreateEffect(CLSID_D2D1Shadow, byRef(shadowEffect)); michael@0: shadowEffect->SetInput(0, image); michael@0: shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma); michael@0: D2D1_VECTOR_4F color = { aColor.r, aColor.g, aColor.b, aColor.a }; michael@0: shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color); michael@0: michael@0: // Step 2, move the shadow effect into place. michael@0: RefPtr affineTransformEffect; michael@0: mDC->CreateEffect(CLSID_D2D12DAffineTransform, byRef(affineTransformEffect)); michael@0: affineTransformEffect->SetInputEffect(0, shadowEffect); michael@0: D2D1_MATRIX_3X2_F matrix = D2D1::Matrix3x2F::Translation(aOffset.x, aOffset.y); michael@0: affineTransformEffect->SetValue(D2D1_2DAFFINETRANSFORM_PROP_TRANSFORM_MATRIX, matrix); michael@0: michael@0: // Step 3, create an effect that combines shadow and bitmap in one image. michael@0: RefPtr compositeEffect; michael@0: mDC->CreateEffect(CLSID_D2D1Composite, byRef(compositeEffect)); michael@0: compositeEffect->SetInputEffect(0, affineTransformEffect); michael@0: compositeEffect->SetInput(1, image); michael@0: compositeEffect->SetValue(D2D1_COMPOSITE_PROP_MODE, D2DCompositionMode(aOperator)); michael@0: michael@0: D2D1_POINT_2F surfPoint = D2DPoint(aDest); michael@0: mDC->DrawImage(compositeEffect, &surfPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::ClearRect(const Rect &aRect) michael@0: { michael@0: MarkChanged(); michael@0: michael@0: mDC->PushAxisAlignedClip(D2DRect(aRect), D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); michael@0: mDC->Clear(); michael@0: mDC->PopAxisAlignedClip(); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: RefPtr image = GetImageForSurface(aMask, ExtendMode::CLAMP); michael@0: michael@0: PrepareForDrawing(aOptions.mCompositionOp, aSource); michael@0: michael@0: // FillOpacityMask only works if the antialias mode is MODE_ALIASED michael@0: mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); michael@0: michael@0: IntSize size = aMask->GetSize(); michael@0: Rect maskRect = Rect(0.f, 0.f, Float(size.width), Float(size.height)); michael@0: image->QueryInterface((ID2D1Bitmap**)&bitmap); michael@0: if (!bitmap) { michael@0: gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces."; michael@0: return; michael@0: } michael@0: michael@0: Rect dest = Rect(aOffset.x, aOffset.y, Float(size.width), Float(size.height)); michael@0: RefPtr brush = CreateBrushForPattern(aSource, aOptions.mAlpha); michael@0: mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect)); michael@0: michael@0: mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aSource); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::CopySurface(SourceSurface *aSurface, michael@0: const IntRect &aSourceRect, michael@0: const IntPoint &aDestination) michael@0: { michael@0: MarkChanged(); michael@0: michael@0: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: Matrix mat; michael@0: RefPtr image = GetImageForSurface(aSurface, mat, ExtendMode::CLAMP); michael@0: michael@0: if (!mat.IsIdentity()) { michael@0: gfxDebug() << *this << ": At this point complex partial uploads are not supported for CopySurface."; michael@0: return; michael@0: } michael@0: michael@0: mDC->DrawImage(image, D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)), michael@0: D2D1::RectF(Float(aSourceRect.x), Float(aSourceRect.y), michael@0: Float(aSourceRect.XMost()), Float(aSourceRect.YMost())), michael@0: D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::FillRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: mDC->FillRectangle(D2DRect(aRect), brush); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::StrokeRect(const Rect &aRect, michael@0: const Pattern &aPattern, michael@0: const StrokeOptions &aStrokeOptions, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: const PathD2D *d2dPath = static_cast(aPath); michael@0: michael@0: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: RefPtr strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions); michael@0: michael@0: mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: const PathD2D *d2dPath = static_cast(aPath); michael@0: michael@0: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: RefPtr brush = CreateBrushForPattern(aPattern, aOptions.mAlpha); michael@0: michael@0: mDC->FillGeometry(d2dPath->mGeometry, brush); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::FillGlyphs(ScaledFont *aFont, michael@0: const GlyphBuffer &aBuffer, michael@0: const Pattern &aPattern, michael@0: const DrawOptions &aOptions, michael@0: const GlyphRenderingOptions *aRenderingOptions) 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 (aRenderingOptions) { michael@0: if (aRenderingOptions->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(aRenderingOptions)->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: PrepareForDrawing(aOptions.mCompositionOp, aPattern); michael@0: michael@0: bool forceClearType = false; michael@0: if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA && michael@0: aOptions.mCompositionOp == CompositionOp::OP_OVER && aaMode == AntialiasMode::SUBPIXEL) { michael@0: forceClearType = true; michael@0: } michael@0: 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 && !forceClearType) { michael@0: d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; michael@0: } michael@0: michael@0: mDC->SetTextAntialiasMode(d2dAAMode); michael@0: michael@0: if (params != mTextRenderingParams) { michael@0: mDC->SetTextRenderingParams(params); michael@0: mTextRenderingParams = params; 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: mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush); michael@0: } michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aPattern); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::Mask(const Pattern &aSource, michael@0: const Pattern &aMask, michael@0: const DrawOptions &aOptions) michael@0: { michael@0: PrepareForDrawing(aOptions.mCompositionOp, aSource); michael@0: michael@0: RefPtr source = CreateBrushForPattern(aSource, aOptions.mAlpha); michael@0: RefPtr mask = CreateBrushForPattern(aMask, 1.0f); michael@0: mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr, michael@0: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, michael@0: D2D1::IdentityMatrix(), michael@0: 1.0f, mask), michael@0: nullptr); 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: mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source); michael@0: michael@0: mDC->PopLayer(); michael@0: michael@0: FinalizeDrawing(aOptions.mCompositionOp, aSource); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: 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: 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: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: if (mClipsArePushed) { michael@0: PushD2DLayer(mDC, pathD2D->mGeometry, clip.mTransform); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::PushClipRect(const Rect &aRect) michael@0: { 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: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: if (mClipsArePushed) { michael@0: mDC->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: DrawTargetD2D1::PopClip() michael@0: { michael@0: if (mClipsArePushed) { michael@0: if (mPushedClips.back().mPath) { michael@0: mDC->PopLayer(); michael@0: } else { michael@0: mDC->PopAxisAlignedClip(); michael@0: } michael@0: } michael@0: mPushedClips.pop_back(); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::CreateSourceSurfaceFromData(unsigned char *aData, michael@0: const IntSize &aSize, michael@0: int32_t aStride, michael@0: SurfaceFormat aFormat) const michael@0: { michael@0: RefPtr bitmap; michael@0: michael@0: HRESULT hr = mDC->CreateBitmap(D2DIntSize(aSize), aData, aStride, michael@0: D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(aFormat)), michael@0: byRef(bitmap)); michael@0: michael@0: if (!bitmap) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return new SourceSurfaceD2D1(bitmap.get(), mDC, aFormat, aSize); michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const michael@0: { michael@0: RefPtr dt = new DrawTargetD2D1(); michael@0: michael@0: if (!dt->Init(aSize, aFormat)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return dt; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::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() << *this << ": 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() << *this << ": 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: DrawTargetD2D1::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: mDC->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() << *this << ": 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: DrawTargetD2D1::CreateFilter(FilterType aType) michael@0: { michael@0: return FilterNodeD2D1::Create(this, mDC, aType); michael@0: } michael@0: michael@0: bool michael@0: DrawTargetD2D1::Init(const IntSize &aSize, SurfaceFormat aFormat) michael@0: { michael@0: HRESULT hr; michael@0: michael@0: hr = Factory::GetD2D1Device()->CreateDeviceContext(D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS, byRef(mDC)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << ": Error " << hr << " failed to initialize new DeviceContext."; michael@0: return false; michael@0: } michael@0: michael@0: D2D1_BITMAP_PROPERTIES1 props; michael@0: props.dpiX = 96; michael@0: props.dpiY = 96; michael@0: props.pixelFormat = D2DPixelFormat(aFormat); michael@0: props.colorContext = nullptr; michael@0: props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET; michael@0: mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mBitmap)); michael@0: michael@0: if (FAILED(hr)) { michael@0: gfxWarning() << *this << ": Error " << hr << " failed to create new CommandList."; michael@0: return false; michael@0: } michael@0: michael@0: mDC->CreateBitmap(D2DIntSize(aSize), nullptr, 0, props, (ID2D1Bitmap1**)byRef(mTempBitmap)); michael@0: michael@0: mDC->SetTarget(mBitmap); michael@0: michael@0: mDC->BeginDraw(); michael@0: michael@0: mFormat = aFormat; michael@0: mSize = aSize; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: /** michael@0: * Private helpers. michael@0: */ michael@0: uint32_t michael@0: DrawTargetD2D1::GetByteSize() const michael@0: { michael@0: return mSize.width * mSize.height * BytesPerPixel(mFormat); michael@0: } michael@0: michael@0: ID2D1Factory1* michael@0: DrawTargetD2D1::factory() michael@0: { michael@0: if (mFactory) { michael@0: return mFactory; michael@0: } michael@0: michael@0: HRESULT hr = D2DFactory()->QueryInterface((ID2D1Factory1**)&mFactory); michael@0: michael@0: if (FAILED(hr)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: RadialGradientEffectD2D1::Register(mFactory); michael@0: michael@0: return mFactory; michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::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: void michael@0: DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp, const Pattern &aPattern) michael@0: { michael@0: MarkChanged(); michael@0: michael@0: // It's important to do this before FlushTransformToDC! As this will cause michael@0: // the transform to become dirty. michael@0: if (!mClipsArePushed) { michael@0: mClipsArePushed = true; michael@0: PushClipsToDC(mDC); michael@0: } michael@0: michael@0: FlushTransformToDC(); michael@0: michael@0: if (aOp == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) { michael@0: return; michael@0: } michael@0: michael@0: mDC->SetTarget(mTempBitmap); michael@0: mDC->Clear(D2D1::ColorF(0, 0)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern) michael@0: { michael@0: bool patternSupported = IsPatternSupportedByD2D(aPattern); michael@0: michael@0: if (aOp == CompositionOp::OP_OVER && patternSupported) { michael@0: return; michael@0: } michael@0: michael@0: RefPtr image; michael@0: mDC->GetTarget(byRef(image)); michael@0: michael@0: mDC->SetTarget(mBitmap); michael@0: michael@0: if (patternSupported) { michael@0: mDC->DrawImage(image, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); michael@0: return; michael@0: } michael@0: michael@0: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: RefPtr radialGradientEffect; michael@0: michael@0: mDC->CreateEffect(CLSID_RadialGradientEffect, byRef(radialGradientEffect)); michael@0: const RadialGradientPattern *pat = static_cast(&aPattern); michael@0: michael@0: radialGradientEffect->SetValue(RADIAL_PROP_STOP_COLLECTION, michael@0: static_cast(pat->mStops.get())->mStopCollection); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y)); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y)); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2); michael@0: radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM, D2DMatrix(pat->mMatrix * mTransform)); michael@0: radialGradientEffect->SetInput(0, image); michael@0: michael@0: mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR, D2DCompositionMode(aOp)); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* 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: void michael@0: DrawTargetD2D1::PopAllClips() michael@0: { michael@0: if (mClipsArePushed) { michael@0: PopClipsFromDC(mDC); michael@0: michael@0: mClipsArePushed = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext *aDC) michael@0: { michael@0: mDC->SetTransform(D2D1::IdentityMatrix()); michael@0: mTransformDirty = true; michael@0: michael@0: for (std::vector::iterator iter = mPushedClips.begin(); michael@0: iter != mPushedClips.end(); iter++) { michael@0: if (iter->mPath) { michael@0: PushD2DLayer(aDC, iter->mPath->mGeometry, iter->mTransform); michael@0: } else { michael@0: mDC->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: DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext *aDC) michael@0: { michael@0: for (int i = mPushedClips.size() - 1; i >= 0; i--) { michael@0: if (mPushedClips[i].mPath) { michael@0: aDC->PopLayer(); michael@0: } else { michael@0: aDC->PopAxisAlignedClip(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha) michael@0: { michael@0: if (!IsPatternSupportedByD2D(aPattern)) { michael@0: RefPtr colBrush; michael@0: mDC->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: mDC->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: mDC->CreateSolidColorBrush(d2dStops.back().color, michael@0: D2D1::BrushProperties(aAlpha), michael@0: byRef(colBrush)); michael@0: return colBrush; michael@0: } michael@0: michael@0: mDC->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: mDC->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: 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: michael@0: Matrix mat = pat->mMatrix; michael@0: michael@0: RefPtr imageBrush; michael@0: RefPtr image = GetImageForSurface(pat->mSurface, mat, pat->mExtendMode); michael@0: mDC->CreateImageBrush(image, michael@0: D2D1::ImageBrushProperties(D2D1::RectF(0, 0, michael@0: Float(pat->mSurface->GetSize().width), michael@0: Float(pat->mSurface->GetSize().height)), michael@0: D2DExtend(pat->mExtendMode), D2DExtend(pat->mExtendMode), michael@0: D2DInterpolationMode(pat->mFilter)), michael@0: D2D1::BrushProperties(aAlpha, D2DMatrix(mat)), michael@0: byRef(imageBrush)); michael@0: return imageBrush; michael@0: } michael@0: michael@0: gfxWarning() << "Invalid pattern type detected."; michael@0: return nullptr; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::GetImageForSurface(SourceSurface *aSurface, Matrix &aSourceTransform, michael@0: ExtendMode aExtendMode) michael@0: { michael@0: RefPtr image; michael@0: michael@0: switch (aSurface->GetType()) { michael@0: case SurfaceType::D2D1_1_IMAGE: michael@0: { michael@0: SourceSurfaceD2D1 *surf = static_cast(aSurface); michael@0: image = surf->GetImage(); michael@0: AddDependencyOnSource(surf); michael@0: } michael@0: break; michael@0: default: michael@0: { michael@0: RefPtr dataSurf = aSurface->GetDataSurface(); michael@0: if (!dataSurf) { michael@0: gfxWarning() << "Invalid surface type."; michael@0: return nullptr; michael@0: } michael@0: michael@0: image = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, aExtendMode, michael@0: aSourceTransform, mDC); michael@0: michael@0: return image; michael@0: } michael@0: break; michael@0: } michael@0: michael@0: return image; michael@0: } michael@0: michael@0: TemporaryRef michael@0: DrawTargetD2D1::OptimizeSourceSurface(SourceSurface* aSurface) const michael@0: { michael@0: if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) { 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 bitmap; michael@0: HRESULT hr = mDC->CreateBitmap(D2DIntSize(data->GetSize()), map.mData, map.mStride, michael@0: D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE, D2DPixelFormat(data->GetFormat())), michael@0: byRef(bitmap)); michael@0: michael@0: data->Unmap(); michael@0: michael@0: if (!bitmap) { michael@0: return data; michael@0: } michael@0: michael@0: return new SourceSurfaceD2D1(bitmap.get(), mDC, data->GetFormat(), data->GetSize()); michael@0: } michael@0: michael@0: void michael@0: DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext *aDC, ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform) michael@0: { michael@0: D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE; michael@0: michael@0: if (aDC->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) { michael@0: options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND; michael@0: } michael@0: michael@0: mDC->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry, michael@0: D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform, michael@0: 1.0, nullptr, options), nullptr); michael@0: } michael@0: michael@0: } michael@0: }