1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
10 #include "DrawTargetD2D1.h"
11 #include "FilterNodeSoftware.h"
12 #include "GradientStopsD2D.h"
13 #include "SourceSurfaceD2D1.h"
14 #include "ConicGradientEffectD2D1.h"
15 #include "RadialGradientEffectD2D1.h"
17 #include "HelpersD2D.h"
18 #include "FilterNodeD2D1.h"
19 #include "ExtendInputEffectD2D1.h"
20 #include "nsAppRunner.h"
21 #include "MainThreadUtils.h"
23 #include "mozilla/Mutex.h"
25 // decltype is not usable for overloaded functions.
26 typedef HRESULT(WINAPI
* D2D1CreateFactoryFunc
)(
27 D2D1_FACTORY_TYPE factoryType
, REFIID iid
,
28 CONST D2D1_FACTORY_OPTIONS
* pFactoryOptions
, void** factory
);
33 uint64_t DrawTargetD2D1::mVRAMUsageDT
;
34 uint64_t DrawTargetD2D1::mVRAMUsageSS
;
35 StaticRefPtr
<ID2D1Factory1
> DrawTargetD2D1::mFactory
;
37 const D2D1_MATRIX_5X4_F kLuminanceMatrix
=
38 D2D1::Matrix5x4F(0, 0, 0, 0.2125f
, 0, 0, 0, 0.7154f
, 0, 0, 0, 0.0721f
, 0, 0,
41 RefPtr
<ID2D1Factory1
> D2DFactory() { return DrawTargetD2D1::factory(); }
43 DrawTargetD2D1::DrawTargetD2D1()
45 mSnapshotLock(std::make_shared
<Mutex
>("DrawTargetD2D1::mSnapshotLock")),
46 mUsedCommandListsSincePurge(0),
47 mTransformedGlyphsSinceLastPurge(0),
48 mComplexBlendsWithListInList(0),
50 mInitState(InitState::Uninitialized
) {}
52 DrawTargetD2D1::~DrawTargetD2D1() {
56 MutexAutoLock
lock(*mSnapshotLock
);
57 // We may hold the only reference. MarkIndependent will clear mSnapshot;
58 // keep the snapshot object alive so it doesn't get destroyed while
59 // MarkIndependent is running.
60 RefPtr
<SourceSurfaceD2D1
> deathGrip
= mSnapshot
;
61 // mSnapshot can be treated as independent of this DrawTarget since we know
62 // this DrawTarget won't change again.
63 deathGrip
->MarkIndependent();
64 // mSnapshot will be cleared now.
67 if (mDC
&& IsDeviceContextValid()) {
68 // The only way mDC can be null is if Init failed, but it can happen and the
69 // destructor is the only place where we need to check for it since the
70 // DrawTarget will destroyed right after Init fails.
75 // Until this point in the destructor it -must- still be valid for
76 // FlushInternal to be called on this.
77 StaticMutexAutoLock
lock(Factory::mDTDependencyLock
);
78 // Targets depending on us can break that dependency, since we're obviously
79 // not going to be modified in the future.
80 for (auto iter
= mDependentTargets
.begin(); iter
!= mDependentTargets
.end();
82 (*iter
)->mDependingOnTargets
.erase(this);
84 // Our dependencies on other targets no longer matter.
85 for (TargetSet::iterator iter
= mDependingOnTargets
.begin();
86 iter
!= mDependingOnTargets
.end(); iter
++) {
87 (*iter
)->mDependentTargets
.erase(this);
92 bool DrawTargetD2D1::IsValid() const {
93 if (mInitState
!= InitState::Uninitialized
&& !IsDeviceContextValid()) {
96 if (NS_IsMainThread()) {
97 // Uninitialized DTs are considered valid.
98 return mInitState
!= InitState::Failure
;
100 return const_cast<DrawTargetD2D1
*>(this)->EnsureInitialized();
104 already_AddRefed
<SourceSurface
> DrawTargetD2D1::Snapshot() {
105 if (!EnsureInitialized()) {
109 MutexAutoLock
lock(*mSnapshotLock
);
111 RefPtr
<SourceSurface
> snapshot(mSnapshot
);
112 return snapshot
.forget();
118 mSnapshot
= new SourceSurfaceD2D1(mBitmap
, mDC
, mFormat
, mSize
, this);
120 RefPtr
<SourceSurface
> snapshot(mSnapshot
);
121 return snapshot
.forget();
124 bool DrawTargetD2D1::EnsureLuminanceEffect() {
125 if (mLuminanceEffect
.get()) {
129 HRESULT hr
= mDC
->CreateEffect(CLSID_D2D1ColorMatrix
,
130 getter_AddRefs(mLuminanceEffect
));
132 gfxCriticalError() << "Failed to create luminance effect. Code: "
137 mLuminanceEffect
->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX
,
139 mLuminanceEffect
->SetValue(D2D1_COLORMATRIX_PROP_ALPHA_MODE
,
140 D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT
);
144 already_AddRefed
<SourceSurface
> DrawTargetD2D1::IntoLuminanceSource(
145 LuminanceType aLuminanceType
, float aOpacity
) {
146 if (!EnsureInitialized()) {
149 if ((aLuminanceType
!= LuminanceType::LUMINANCE
) ||
150 // See bug 1372577, some race condition where we get invalid
151 // results with D2D in the parent process. Fallback in that case.
152 XRE_IsParentProcess()) {
153 return DrawTarget::IntoLuminanceSource(aLuminanceType
, aOpacity
);
156 // Create the luminance effect
157 if (!EnsureLuminanceEffect()) {
158 return DrawTarget::IntoLuminanceSource(aLuminanceType
, aOpacity
);
164 D2D1_MATRIX_5X4_F matrix
= kLuminanceMatrix
;
165 matrix
._14
*= aOpacity
;
166 matrix
._24
*= aOpacity
;
167 matrix
._34
*= aOpacity
;
169 mLuminanceEffect
->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX
, matrix
);
172 mLuminanceEffect
->SetInput(0, mBitmap
);
174 RefPtr
<ID2D1Image
> luminanceOutput
;
175 mLuminanceEffect
->GetOutput(getter_AddRefs(luminanceOutput
));
177 return MakeAndAddRef
<SourceSurfaceD2D1
>(luminanceOutput
, mDC
,
178 SurfaceFormat::B8G8R8A8
, mSize
);
181 // Command lists are kept around by device contexts until EndDraw is called,
182 // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
183 // are expensive though, especially relatively when little work is done, so
184 // we try to reduce the amount of times we execute these purges.
185 static const uint32_t kPushedLayersBeforePurge
= 25;
186 // Rendering glyphs with different transforms causes the glyph cache to grow
187 // very large (see bug 1474883) so we must call EndDraw every so often.
188 static const uint32_t kTransformedGlyphsBeforePurge
= 1000;
190 void DrawTargetD2D1::Flush() { FlushInternal(); }
192 bool DrawTargetD2D1::MaybeClearRect(CompositionOp aOp
, const Rect
& aBounds
) {
193 if (aOp
== CompositionOp::OP_CLEAR
) {
194 FillRect(aBounds
, ColorPattern(DeviceColor(1.0f
, 1.0f
, 1.0f
, 1.0f
)),
195 DrawOptions(1.0f
, aOp
));
201 void DrawTargetD2D1::DrawSurface(SourceSurface
* aSurface
, const Rect
& aDest
,
203 const DrawSurfaceOptions
& aSurfOptions
,
204 const DrawOptions
& aOptions
) {
205 if (MaybeClearRect(aOptions
.mCompositionOp
, aDest
)) {
209 if (!PrepareForDrawing(aOptions
.mCompositionOp
,
210 ColorPattern(DeviceColor()))) {
214 Rect source
= aSource
- aSurface
->GetRect().TopLeft();
216 D2D1_RECT_F samplingBounds
;
218 if (aSurfOptions
.mSamplingBounds
== SamplingBounds::BOUNDED
) {
219 samplingBounds
= D2DRect(source
);
221 samplingBounds
= D2D1::RectF(0, 0, Float(aSurface
->GetSize().width
),
222 Float(aSurface
->GetSize().height
));
225 Float xScale
= aDest
.Width() / source
.Width();
226 Float yScale
= aDest
.Height() / source
.Height();
228 RefPtr
<ID2D1ImageBrush
> brush
;
230 // Here we scale the source pattern up to the size and position where we want
233 transform
.PreTranslate(aDest
.X() - source
.X() * xScale
,
234 aDest
.Y() - source
.Y() * yScale
);
235 transform
.PreScale(xScale
, yScale
);
237 RefPtr
<ID2D1Image
> image
=
238 GetImageForSurface(aSurface
, transform
, ExtendMode::CLAMP
);
241 gfxWarning() << *this << ": Unable to get D2D image for surface.";
245 RefPtr
<ID2D1Bitmap
> bitmap
;
247 if (aSurface
->GetType() == SurfaceType::D2D1_1_IMAGE
) {
248 // If this is called with a DataSourceSurface it might do a partial upload
249 // that our DrawBitmap call doesn't support.
250 hr
= image
->QueryInterface((ID2D1Bitmap
**)getter_AddRefs(bitmap
));
253 if (SUCCEEDED(hr
) && bitmap
&&
254 aSurfOptions
.mSamplingBounds
== SamplingBounds::UNBOUNDED
) {
255 mDC
->DrawBitmap(bitmap
, D2DRect(aDest
), aOptions
.mAlpha
,
256 D2DFilter(aSurfOptions
.mSamplingFilter
), D2DRect(source
));
258 // This has issues ignoring the alpha channel on windows 7 with images
260 MOZ_ASSERT(aSurface
->GetFormat() != SurfaceFormat::B8G8R8X8
);
262 // Bug 1275478 - D2D1 cannot draw A8 surface correctly.
263 MOZ_ASSERT(aSurface
->GetFormat() != SurfaceFormat::A8
);
265 mDC
->CreateImageBrush(
267 D2D1::ImageBrushProperties(
268 samplingBounds
, D2D1_EXTEND_MODE_CLAMP
, D2D1_EXTEND_MODE_CLAMP
,
269 D2DInterpolationMode(aSurfOptions
.mSamplingFilter
)),
270 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(transform
)),
271 getter_AddRefs(brush
));
272 mDC
->FillRectangle(D2DRect(aDest
), brush
);
275 FinalizeDrawing(aOptions
.mCompositionOp
, ColorPattern(DeviceColor()));
278 void DrawTargetD2D1::DrawFilter(FilterNode
* aNode
, const Rect
& aSourceRect
,
279 const Point
& aDestPoint
,
280 const DrawOptions
& aOptions
) {
281 if (aNode
->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1
) {
282 gfxWarning() << *this << ": Incompatible filter passed to DrawFilter.";
286 if (MaybeClearRect(aOptions
.mCompositionOp
,
287 Rect(aDestPoint
, aSourceRect
.Size()))) {
291 if (!PrepareForDrawing(aOptions
.mCompositionOp
,
292 ColorPattern(DeviceColor()))) {
296 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
298 FilterNodeD2D1
* node
= static_cast<FilterNodeD2D1
*>(aNode
);
299 node
->WillDraw(this);
301 if (aOptions
.mAlpha
== 1.0f
) {
302 mDC
->DrawImage(node
->OutputEffect(), D2DPoint(aDestPoint
),
303 D2DRect(aSourceRect
));
305 RefPtr
<ID2D1Image
> image
;
306 node
->OutputEffect()->GetOutput(getter_AddRefs(image
));
308 Matrix mat
= Matrix::Translation(aDestPoint
);
310 RefPtr
<ID2D1ImageBrush
> imageBrush
;
311 mDC
->CreateImageBrush(
312 image
, D2D1::ImageBrushProperties(D2DRect(aSourceRect
)),
313 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(mat
)),
314 getter_AddRefs(imageBrush
));
315 mDC
->FillRectangle(D2D1::RectF(aDestPoint
.x
, aDestPoint
.y
,
316 aDestPoint
.x
+ aSourceRect
.width
,
317 aDestPoint
.y
+ aSourceRect
.height
),
321 FinalizeDrawing(aOptions
.mCompositionOp
, ColorPattern(DeviceColor()));
324 void DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface
* aSurface
,
326 const ShadowOptions
& aShadow
,
327 CompositionOp aOperator
) {
328 if (!EnsureInitialized()) {
332 if (MaybeClearRect(aOperator
, Rect(aDest
, Size(aSurface
->GetSize())))) {
339 RefPtr
<ID2D1Image
> image
=
340 GetImageForSurface(aSurface
, mat
, ExtendMode::CLAMP
, nullptr, false);
343 gfxWarning() << "Couldn't get image for surface.";
347 if (!mat
.IsIdentity()) {
349 << ": At this point complex partial uploads are not supported "
350 "for Shadow surfaces.";
354 if (!PrepareForDrawing(aOperator
, ColorPattern(aShadow
.mColor
))) {
358 mDC
->SetTransform(D2D1::IdentityMatrix());
359 mTransformDirty
= true;
361 RefPtr
<ID2D1Effect
> shadowEffect
;
362 HRESULT hr
= mDC
->CreateEffect(
363 mFormat
== SurfaceFormat::A8
? CLSID_D2D1GaussianBlur
: CLSID_D2D1Shadow
,
364 getter_AddRefs(shadowEffect
));
365 if (SUCCEEDED(hr
) && shadowEffect
) {
366 shadowEffect
->SetInput(0, image
);
367 if (mFormat
== SurfaceFormat::A8
) {
368 shadowEffect
->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION
,
370 shadowEffect
->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE
,
371 D2D1_BORDER_MODE_HARD
);
373 shadowEffect
->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION
,
375 D2D1_VECTOR_4F color
= {aShadow
.mColor
.r
, aShadow
.mColor
.g
,
376 aShadow
.mColor
.b
, aShadow
.mColor
.a
};
377 shadowEffect
->SetValue(D2D1_SHADOW_PROP_COLOR
, color
);
380 D2D1_POINT_2F shadowPoint
= D2DPoint(aDest
+ aShadow
.mOffset
);
381 mDC
->DrawImage(shadowEffect
, &shadowPoint
, nullptr,
382 D2D1_INTERPOLATION_MODE_LINEAR
,
383 D2D1_COMPOSITE_MODE_SOURCE_OVER
);
385 gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr
);
388 if (aSurface
->GetFormat() != SurfaceFormat::A8
) {
389 D2D1_POINT_2F imgPoint
= D2DPoint(aDest
);
390 mDC
->DrawImage(image
, &imgPoint
, nullptr, D2D1_INTERPOLATION_MODE_LINEAR
,
391 D2D1_COMPOSITE_MODE_SOURCE_OVER
);
394 FinalizeDrawing(aOperator
, ColorPattern(aShadow
.mColor
));
397 void DrawTargetD2D1::ClearRect(const Rect
& aRect
) {
398 if (!EnsureInitialized()) {
402 if (aRect
.IsEmpty()) {
403 // Nothing to be done.
413 if (mTransformDirty
|| !mTransform
.IsIdentity()) {
414 mDC
->SetTransform(D2D1::IdentityMatrix());
415 mTransformDirty
= true;
418 D2D1_RECT_F clipRect
;
420 if (mTransform
.IsRectilinear() &&
421 GetDeviceSpaceClipRect(clipRect
, isPixelAligned
)) {
422 mDC
->PushAxisAlignedClip(clipRect
, isPixelAligned
423 ? D2D1_ANTIALIAS_MODE_ALIASED
424 : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
426 mDC
->PopAxisAlignedClip();
432 RefPtr
<ID2D1CommandList
> list
;
433 mUsedCommandListsSincePurge
++;
434 mDC
->CreateCommandList(getter_AddRefs(list
));
435 mDC
->SetTarget(list
);
438 RefPtr
<ID2D1Geometry
> geom
= GetClippedGeometry(&addClipRect
);
440 RefPtr
<ID2D1SolidColorBrush
> brush
;
441 mDC
->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White
),
442 getter_AddRefs(brush
));
443 mDC
->PushAxisAlignedClip(
444 D2D1::RectF(addClipRect
.X(), addClipRect
.Y(), addClipRect
.XMost(),
445 addClipRect
.YMost()),
446 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
447 mDC
->FillGeometry(geom
, brush
);
448 mDC
->PopAxisAlignedClip();
450 mDC
->SetTarget(CurrentTarget());
453 mDC
->DrawImage(list
, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
454 D2D1_COMPOSITE_MODE_DESTINATION_OUT
);
461 void DrawTargetD2D1::MaskSurface(const Pattern
& aSource
, SourceSurface
* aMask
,
462 Point aOffset
, const DrawOptions
& aOptions
) {
463 if (!EnsureInitialized()) {
468 RefPtr
<ID2D1Bitmap
> bitmap
;
470 Matrix mat
= Matrix::Translation(aOffset
);
471 RefPtr
<ID2D1Image
> image
=
472 GetImageForSurface(aMask
, mat
, ExtendMode::CLAMP
, nullptr);
474 MOZ_ASSERT(!mat
.HasNonTranslation());
479 gfxWarning() << "Failed to get image for surface.";
483 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aSource
)) {
488 IntSize::Truncate(aMask
->GetSize().width
, aMask
->GetSize().height
);
490 Rect(aOffset
.x
+ aMask
->GetRect().x
, aOffset
.y
+ aMask
->GetRect().y
,
491 Float(size
.width
), Float(size
.height
));
493 HRESULT hr
= image
->QueryInterface((ID2D1Bitmap
**)getter_AddRefs(bitmap
));
494 if (!bitmap
|| FAILED(hr
)) {
495 // D2D says if we have an actual ID2D1Image and not a bitmap underlying the
496 // object, we can't query for a bitmap. Instead, Push/PopLayer
497 gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces. "
498 "Falling back to push/pop layer";
500 RefPtr
<ID2D1Brush
> source
= CreateBrushForPattern(aSource
, aOptions
);
501 RefPtr
<ID2D1ImageBrush
> maskBrush
;
502 hr
= mDC
->CreateImageBrush(
504 D2D1::ImageBrushProperties(D2D1::RectF(0, 0, size
.width
, size
.height
)),
505 D2D1::BrushProperties(
506 1.0f
, D2D1::Matrix3x2F::Translation(aMask
->GetRect().x
,
507 aMask
->GetRect().y
)),
508 getter_AddRefs(maskBrush
));
509 MOZ_ASSERT(SUCCEEDED(hr
));
512 D2D1::LayerParameters1(D2D1::InfiniteRect(), nullptr,
513 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
,
514 D2D1::Matrix3x2F::Translation(
515 aMask
->GetRect().x
, aMask
->GetRect().y
),
516 1.0f
, maskBrush
, D2D1_LAYER_OPTIONS1_NONE
),
519 mDC
->FillRectangle(D2DRect(dest
), source
);
522 FinalizeDrawing(aOptions
.mCompositionOp
, aSource
);
525 // If this is a data source surface, we might have created a partial bitmap
526 // for this surface and only uploaded part of the mask. In that case,
527 // we have to fixup our sizes here.
528 size
.width
= bitmap
->GetSize().width
;
529 size
.height
= bitmap
->GetSize().height
;
530 dest
.SetWidth(size
.width
);
531 dest
.SetHeight(size
.height
);
534 // FillOpacityMask only works if the antialias mode is MODE_ALIASED
535 mDC
->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED
);
537 Rect maskRect
= Rect(aMask
->GetRect().x
, aMask
->GetRect().y
,
538 Float(size
.width
), Float(size
.height
));
539 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aSource
, aOptions
);
540 mDC
->FillOpacityMask(bitmap
, brush
, D2D1_OPACITY_MASK_CONTENT_GRAPHICS
,
541 D2DRect(dest
), D2DRect(maskRect
));
543 mDC
->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
545 FinalizeDrawing(aOptions
.mCompositionOp
, aSource
);
548 void DrawTargetD2D1::CopySurface(SourceSurface
* aSurface
,
549 const IntRect
& aSourceRect
,
550 const IntPoint
& aDestination
) {
551 if (!EnsureInitialized()) {
558 mDC
->SetTransform(D2D1::IdentityMatrix());
559 mTransformDirty
= true;
561 Matrix mat
= Matrix::Translation(aDestination
.x
- aSourceRect
.X(),
562 aDestination
.y
- aSourceRect
.Y());
563 RefPtr
<ID2D1Image
> image
=
564 GetImageForSurface(aSurface
, mat
, ExtendMode::CLAMP
, nullptr, false);
567 gfxWarning() << "Couldn't get image for surface.";
571 if (mat
.HasNonIntegerTranslation()) {
573 << ": At this point scaled partial uploads are not supported "
578 IntRect sourceRect
= aSourceRect
;
579 sourceRect
.SetLeftEdge(sourceRect
.X() + (aDestination
.x
- aSourceRect
.X()) -
581 sourceRect
.SetTopEdge(sourceRect
.Y() + (aDestination
.y
- aSourceRect
.Y()) -
584 RefPtr
<ID2D1Bitmap
> bitmap
;
585 HRESULT hr
= image
->QueryInterface((ID2D1Bitmap
**)getter_AddRefs(bitmap
));
587 if (SUCCEEDED(hr
) && bitmap
&& mFormat
== SurfaceFormat::A8
) {
588 RefPtr
<ID2D1SolidColorBrush
> brush
;
589 mDC
->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White
),
590 D2D1::BrushProperties(), getter_AddRefs(brush
));
591 mDC
->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED
);
592 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY
);
593 mDC
->FillOpacityMask(bitmap
, brush
, D2D1_OPACITY_MASK_CONTENT_GRAPHICS
);
594 mDC
->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
595 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER
);
599 Rect
srcRect(Float(sourceRect
.X()), Float(sourceRect
.Y()),
600 Float(aSourceRect
.Width()), Float(aSourceRect
.Height()));
602 Rect
dstRect(Float(aDestination
.x
), Float(aDestination
.y
),
603 Float(aSourceRect
.Width()), Float(aSourceRect
.Height()));
605 if (SUCCEEDED(hr
) && bitmap
) {
606 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY
);
607 mDC
->DrawBitmap(bitmap
, D2DRect(dstRect
), 1.0f
,
608 D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
610 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER
);
614 mDC
->DrawImage(image
,
615 D2D1::Point2F(Float(aDestination
.x
), Float(aDestination
.y
)),
616 D2DRect(srcRect
), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
617 D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY
);
620 void DrawTargetD2D1::FillRect(const Rect
& aRect
, const Pattern
& aPattern
,
621 const DrawOptions
& aOptions
) {
622 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
626 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
628 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
629 mDC
->FillRectangle(D2DRect(aRect
), brush
);
631 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
634 void DrawTargetD2D1::FillRoundedRect(const RoundedRect
& aRect
,
635 const Pattern
& aPattern
,
636 const DrawOptions
& aOptions
) {
637 if (!aRect
.corners
.AreRadiiSame()) {
638 return DrawTarget::FillRoundedRect(aRect
, aPattern
, aOptions
);
641 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
645 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
647 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
648 mDC
->FillRoundedRectangle(D2DRoundedRect(aRect
), brush
);
650 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
653 void DrawTargetD2D1::StrokeRect(const Rect
& aRect
, const Pattern
& aPattern
,
654 const StrokeOptions
& aStrokeOptions
,
655 const DrawOptions
& aOptions
) {
656 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
660 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
662 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
663 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
664 CreateStrokeStyleForOptions(aStrokeOptions
);
666 mDC
->DrawRectangle(D2DRect(aRect
), brush
, aStrokeOptions
.mLineWidth
,
669 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
672 void DrawTargetD2D1::StrokeLine(const Point
& aStart
, const Point
& aEnd
,
673 const Pattern
& aPattern
,
674 const StrokeOptions
& aStrokeOptions
,
675 const DrawOptions
& aOptions
) {
676 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
680 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
682 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
683 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
684 CreateStrokeStyleForOptions(aStrokeOptions
);
686 mDC
->DrawLine(D2DPoint(aStart
), D2DPoint(aEnd
), brush
,
687 aStrokeOptions
.mLineWidth
, strokeStyle
);
689 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
691 void DrawTargetD2D1::StrokeCircle(const Point
& aOrigin
, float radius
,
692 const Pattern
& aPattern
,
693 const StrokeOptions
& aStrokeOptions
,
694 const DrawOptions
& aOptions
) {
695 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
699 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
701 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
702 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
703 CreateStrokeStyleForOptions(aStrokeOptions
);
705 mDC
->DrawEllipse(D2D1::Ellipse(D2DPoint(aOrigin
), radius
, radius
), brush
,
706 aStrokeOptions
.mLineWidth
, strokeStyle
);
708 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
711 void DrawTargetD2D1::FillCircle(const Point
& aOrigin
, float radius
,
712 const Pattern
& aPattern
,
713 const DrawOptions
& aOptions
) {
714 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
718 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
720 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
722 mDC
->FillEllipse(D2D1::Ellipse(D2DPoint(aOrigin
), radius
, radius
), brush
);
724 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
727 void DrawTargetD2D1::Stroke(const Path
* aPath
, const Pattern
& aPattern
,
728 const StrokeOptions
& aStrokeOptions
,
729 const DrawOptions
& aOptions
) {
730 const Path
* path
= aPath
;
731 if (path
->GetBackendType() != BackendType::DIRECT2D1_1
) {
732 gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
735 const PathD2D
* d2dPath
= static_cast<const PathD2D
*>(path
);
737 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
741 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
743 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
744 RefPtr
<ID2D1StrokeStyle
> strokeStyle
=
745 CreateStrokeStyleForOptions(aStrokeOptions
);
747 mDC
->DrawGeometry(d2dPath
->mGeometry
, brush
, aStrokeOptions
.mLineWidth
,
750 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
753 void DrawTargetD2D1::Fill(const Path
* aPath
, const Pattern
& aPattern
,
754 const DrawOptions
& aOptions
) {
755 const Path
* path
= aPath
;
756 if (!path
|| path
->GetBackendType() != BackendType::DIRECT2D1_1
) {
757 gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
760 const PathD2D
* d2dPath
= static_cast<const PathD2D
*>(path
);
762 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
766 mDC
->SetAntialiasMode(D2DAAMode(aOptions
.mAntialiasMode
));
768 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
770 mDC
->FillGeometry(d2dPath
->mGeometry
, brush
);
772 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
775 void DrawTargetD2D1::FillGlyphs(ScaledFont
* aFont
, const GlyphBuffer
& aBuffer
,
776 const Pattern
& aPattern
,
777 const DrawOptions
& aOptions
) {
778 if (aFont
->GetType() != FontType::DWRITE
) {
779 gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
783 ScaledFontDWrite
* font
= static_cast<ScaledFontDWrite
*>(aFont
);
785 // May be null, if we failed to initialize the default rendering params.
786 RefPtr
<IDWriteRenderingParams
> params
=
787 font
->DWriteSettings().RenderingParams();
789 AntialiasMode aaMode
= font
->GetDefaultAAMode();
791 if (aOptions
.mAntialiasMode
!= AntialiasMode::DEFAULT
) {
792 aaMode
= aOptions
.mAntialiasMode
;
795 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aPattern
)) {
799 bool forceClearType
= false;
800 if (!CurrentLayer().mIsOpaque
&& mPermitSubpixelAA
&&
801 aOptions
.mCompositionOp
== CompositionOp::OP_OVER
&&
802 aaMode
== AntialiasMode::SUBPIXEL
) {
803 forceClearType
= true;
806 D2D1_TEXT_ANTIALIAS_MODE d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
809 case AntialiasMode::NONE
:
810 d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_ALIASED
;
812 case AntialiasMode::GRAY
:
813 d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
815 case AntialiasMode::SUBPIXEL
:
816 d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
;
819 d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_DEFAULT
;
822 if (d2dAAMode
== D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE
&&
823 !CurrentLayer().mIsOpaque
&& !forceClearType
) {
824 d2dAAMode
= D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE
;
827 mDC
->SetTextAntialiasMode(d2dAAMode
);
829 if (params
!= mTextRenderingParams
) {
831 // https://docs.microsoft.com/en-us/windows/win32/api/d2d1/nf-d2d1-id2d1rendertarget-settextrenderingparams
832 // it's OK to pass null for params here; it will just "clear current text
833 // rendering options".
834 mDC
->SetTextRenderingParams(params
);
835 mTextRenderingParams
= params
;
838 RefPtr
<ID2D1Brush
> brush
= CreateBrushForPattern(aPattern
, aOptions
);
840 AutoDWriteGlyphRun autoRun
;
841 DWriteGlyphRunFromGlyphs(aBuffer
, font
, &autoRun
);
843 bool needsRepushedLayers
= false;
844 if (forceClearType
) {
847 needsRepushedLayers
= CurrentLayer().mPushedClips
.size() &&
848 !GetDeviceSpaceClipRect(rect
, isAligned
);
850 // If we have a complex clip in our stack and we have a transparent
851 // background, and subpixel AA is permitted, we need to repush our layer
852 // stack limited by the glyph run bounds initializing our layers for
854 if (needsRepushedLayers
) {
855 mDC
->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun
,
856 DWRITE_MEASURING_MODE_NATURAL
, &rect
);
857 rect
.left
= std::floor(rect
.left
);
858 rect
.right
= std::ceil(rect
.right
);
859 rect
.top
= std::floor(rect
.top
);
860 rect
.bottom
= std::ceil(rect
.bottom
);
864 if (!mTransform
.IsRectilinear()) {
865 // We must limit the pixels we touch to the -user space- bounds of
866 // the glyphs being drawn. In order not to get transparent pixels
867 // copied up in our pushed layer stack.
868 D2D1_RECT_F userRect
;
869 mDC
->SetTransform(D2D1::IdentityMatrix());
870 mDC
->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun
,
871 DWRITE_MEASURING_MODE_NATURAL
, &userRect
);
873 RefPtr
<ID2D1PathGeometry
> path
;
874 factory()->CreatePathGeometry(getter_AddRefs(path
));
875 RefPtr
<ID2D1GeometrySink
> sink
;
876 path
->Open(getter_AddRefs(sink
));
877 AddRectToSink(sink
, userRect
);
881 D2D1::LayerParameters1(
882 D2D1::InfiniteRect(), path
, D2D1_ANTIALIAS_MODE_ALIASED
,
883 D2DMatrix(mTransform
), 1.0f
, nullptr,
884 D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND
|
885 D2D1_LAYER_OPTIONS1_IGNORE_ALPHA
),
889 PushClipsToDC(mDC
, true, rect
);
890 mDC
->SetTransform(D2DMatrix(mTransform
));
895 mDC
->DrawGlyphRun(D2D1::Point2F(), &autoRun
, brush
);
898 if (mTransform
.HasNonTranslation()) {
899 mTransformedGlyphsSinceLastPurge
+= aBuffer
.mNumGlyphs
;
902 if (needsRepushedLayers
) {
905 if (!mTransform
.IsRectilinear()) {
910 FinalizeDrawing(aOptions
.mCompositionOp
, aPattern
);
913 void DrawTargetD2D1::Mask(const Pattern
& aSource
, const Pattern
& aMask
,
914 const DrawOptions
& aOptions
) {
915 if (!PrepareForDrawing(aOptions
.mCompositionOp
, aSource
)) {
919 RefPtr
<ID2D1Brush
> source
= CreateBrushForPattern(aSource
, aOptions
);
920 RefPtr
<ID2D1Brush
> mask
= CreateBrushForPattern(aMask
, DrawOptions());
921 mDC
->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
922 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
,
923 D2D1::IdentityMatrix(), 1.0f
, mask
),
926 Rect
rect(0, 0, (Float
)mSize
.width
, (Float
)mSize
.height
);
927 Matrix mat
= mTransform
;
930 mDC
->FillRectangle(D2DRect(mat
.TransformBounds(rect
)), source
);
934 FinalizeDrawing(aOptions
.mCompositionOp
, aSource
);
937 void DrawTargetD2D1::PushClipGeometry(ID2D1Geometry
* aGeometry
,
938 const D2D1_MATRIX_3X2_F
& aTransform
,
939 bool aPixelAligned
) {
940 mCurrentClippedGeometry
= nullptr;
943 clip
.mGeometry
= aGeometry
;
944 clip
.mTransform
= aTransform
;
945 clip
.mIsPixelAligned
= aPixelAligned
;
947 aGeometry
->GetBounds(aTransform
, &clip
.mBounds
);
949 CurrentLayer().mPushedClips
.push_back(clip
);
951 // The transform of clips is relative to the world matrix, since we use the
952 // total transform for the clips, make the world matrix identity.
953 mDC
->SetTransform(D2D1::IdentityMatrix());
954 mTransformDirty
= true;
956 if (CurrentLayer().mClipsArePushed
) {
957 PushD2DLayer(mDC
, clip
.mGeometry
, clip
.mTransform
, clip
.mIsPixelAligned
);
961 void DrawTargetD2D1::PushClip(const Path
* aPath
) {
962 const Path
* path
= aPath
;
963 if (path
->GetBackendType() != BackendType::DIRECT2D1_1
) {
964 gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
967 if (!EnsureInitialized()) {
971 RefPtr
<PathD2D
> pathD2D
= static_cast<PathD2D
*>(const_cast<Path
*>(path
));
973 PushClipGeometry(pathD2D
->GetGeometry(), D2DMatrix(mTransform
));
976 void DrawTargetD2D1::PushClipRect(const Rect
& aRect
) {
977 if (!EnsureInitialized()) {
980 if (!mTransform
.IsRectilinear()) {
981 // Whoops, this isn't a rectangle in device space, Direct2D will not deal
982 // with this transform the way we want it to.
984 // http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
985 RefPtr
<ID2D1Geometry
> geom
= ConvertRectToGeometry(D2DRect(aRect
));
986 return PushClipGeometry(geom
, D2DMatrix(mTransform
));
989 mCurrentClippedGeometry
= nullptr;
992 Rect rect
= mTransform
.TransformBounds(aRect
);
994 clip
.mIsPixelAligned
= rect
.ToIntRect(&intRect
);
996 // Do not store the transform, just store the device space rectangle directly.
997 clip
.mBounds
= D2DRect(rect
);
999 CurrentLayer().mPushedClips
.push_back(clip
);
1001 mDC
->SetTransform(D2D1::IdentityMatrix());
1002 mTransformDirty
= true;
1004 if (CurrentLayer().mClipsArePushed
) {
1005 mDC
->PushAxisAlignedClip(
1006 clip
.mBounds
, clip
.mIsPixelAligned
? D2D1_ANTIALIAS_MODE_ALIASED
1007 : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
1011 void DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect
* aRects
,
1013 if (!EnsureInitialized()) {
1016 // Build a path for the union of the rects.
1017 RefPtr
<ID2D1PathGeometry
> path
;
1018 factory()->CreatePathGeometry(getter_AddRefs(path
));
1019 RefPtr
<ID2D1GeometrySink
> sink
;
1020 path
->Open(getter_AddRefs(sink
));
1021 sink
->SetFillMode(D2D1_FILL_MODE_WINDING
);
1022 for (uint32_t i
= 0; i
< aCount
; i
++) {
1023 const IntRect
& rect
= aRects
[i
];
1024 sink
->BeginFigure(D2DPoint(rect
.TopLeft()), D2D1_FIGURE_BEGIN_FILLED
);
1025 D2D1_POINT_2F lines
[3] = {D2DPoint(rect
.TopRight()),
1026 D2DPoint(rect
.BottomRight()),
1027 D2DPoint(rect
.BottomLeft())};
1028 sink
->AddLines(lines
, 3);
1029 sink
->EndFigure(D2D1_FIGURE_END_CLOSED
);
1033 // The path is in device-space, so there is no transform needed,
1034 // and all rects are pixel aligned.
1035 PushClipGeometry(path
, D2D1::IdentityMatrix(), true);
1038 void DrawTargetD2D1::PopClip() {
1039 if (!EnsureInitialized()) {
1042 mCurrentClippedGeometry
= nullptr;
1043 if (CurrentLayer().mPushedClips
.empty()) {
1044 gfxDevCrash(LogReason::UnbalancedClipStack
)
1045 << "DrawTargetD2D1::PopClip: No clip to pop.";
1049 if (CurrentLayer().mClipsArePushed
) {
1050 if (CurrentLayer().mPushedClips
.back().mGeometry
) {
1053 mDC
->PopAxisAlignedClip();
1056 CurrentLayer().mPushedClips
.pop_back();
1059 bool DrawTargetD2D1::RemoveAllClips() {
1060 if (!EnsureInitialized()) {
1063 mCurrentClippedGeometry
= nullptr;
1064 while (!CurrentLayer().mPushedClips
.empty()) {
1070 void DrawTargetD2D1::PushLayer(bool aOpaque
, Float aOpacity
,
1071 SourceSurface
* aMask
,
1072 const Matrix
& aMaskTransform
,
1073 const IntRect
& aBounds
, bool aCopyBackground
) {
1074 if (!EnsureInitialized()) {
1077 D2D1_LAYER_OPTIONS1 options
= D2D1_LAYER_OPTIONS1_NONE
;
1080 options
|= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA
;
1082 if (aCopyBackground
) {
1083 options
|= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND
;
1086 RefPtr
<ID2D1ImageBrush
> mask
;
1087 Matrix maskTransform
= aMaskTransform
;
1088 RefPtr
<ID2D1PathGeometry
> clip
;
1091 RefPtr
<ID2D1Image
> image
=
1092 GetImageForSurface(aMask
, maskTransform
, ExtendMode::CLAMP
);
1093 mDC
->SetTransform(D2D1::IdentityMatrix());
1094 mTransformDirty
= true;
1097 maskTransform
.PreTranslate(aMask
->GetRect().X(), aMask
->GetRect().Y());
1098 // The mask is given in user space. Our layer will apply it in device space.
1099 maskTransform
= maskTransform
* mTransform
;
1102 IntSize maskSize
= aMask
->GetSize();
1103 HRESULT hr
= mDC
->CreateImageBrush(
1105 D2D1::ImageBrushProperties(
1106 D2D1::RectF(0, 0, maskSize
.width
, maskSize
.height
)),
1107 D2D1::BrushProperties(1.0f
, D2DMatrix(maskTransform
)),
1108 getter_AddRefs(mask
));
1110 gfxWarning() << "[D2D1.1] Failed to create a ImageBrush, code: "
1114 factory()->CreatePathGeometry(getter_AddRefs(clip
));
1115 RefPtr
<ID2D1GeometrySink
> sink
;
1116 clip
->Open(getter_AddRefs(sink
));
1117 AddRectToSink(sink
, D2D1::RectF(0, 0, aMask
->GetSize().width
,
1118 aMask
->GetSize().height
));
1121 gfxCriticalError() << "Failed to get image for mask surface!";
1127 mDC
->PushLayer(D2D1::LayerParameters1(
1128 D2D1::InfiniteRect(), clip
, D2D1_ANTIALIAS_MODE_ALIASED
,
1129 D2DMatrix(maskTransform
), aOpacity
, mask
, options
),
1131 PushedLayer pushedLayer
;
1132 pushedLayer
.mClipsArePushed
= false;
1133 pushedLayer
.mIsOpaque
= aOpaque
;
1134 pushedLayer
.mOldPermitSubpixelAA
= mPermitSubpixelAA
;
1135 mPermitSubpixelAA
= aOpaque
;
1137 mDC
->CreateCommandList(getter_AddRefs(pushedLayer
.mCurrentList
));
1138 mPushedLayers
.push_back(pushedLayer
);
1140 mDC
->SetTarget(CurrentTarget());
1142 mUsedCommandListsSincePurge
++;
1145 void DrawTargetD2D1::PopLayer() {
1146 // We must have at least one layer at all times.
1147 MOZ_ASSERT(mPushedLayers
.size() > 1);
1148 MOZ_ASSERT(CurrentLayer().mPushedClips
.size() == 0);
1149 if (!EnsureInitialized() || mPushedLayers
.size() <= 1) {
1152 RefPtr
<ID2D1CommandList
> list
= CurrentLayer().mCurrentList
;
1153 mPermitSubpixelAA
= CurrentLayer().mOldPermitSubpixelAA
;
1155 mPushedLayers
.pop_back();
1156 mDC
->SetTarget(CurrentTarget());
1159 mDC
->SetTransform(D2D1::IdentityMatrix());
1160 mTransformDirty
= true;
1162 DCCommandSink
sink(mDC
);
1163 list
->Stream(&sink
);
1165 mComplexBlendsWithListInList
= 0;
1170 already_AddRefed
<SourceSurface
> DrawTargetD2D1::CreateSourceSurfaceFromData(
1171 unsigned char* aData
, const IntSize
& aSize
, int32_t aStride
,
1172 SurfaceFormat aFormat
) const {
1173 RefPtr
<ID2D1Bitmap1
> bitmap
;
1175 RefPtr
<ID2D1DeviceContext
> dc
= Factory::GetD2DDeviceContext();
1181 dc
->CreateBitmap(D2DIntSize(aSize
), aData
, aStride
,
1182 D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE
,
1183 D2DPixelFormat(aFormat
)),
1184 getter_AddRefs(bitmap
));
1186 if (FAILED(hr
) || !bitmap
) {
1188 CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize
)))
1189 << "[D2D1.1] 1CreateBitmap failure " << aSize
<< " Code: " << hexa(hr
)
1190 << " format " << (int)aFormat
;
1194 return MakeAndAddRef
<SourceSurfaceD2D1
>(bitmap
.get(), dc
.get(), aFormat
,
1198 already_AddRefed
<DrawTarget
> DrawTargetD2D1::CreateSimilarDrawTarget(
1199 const IntSize
& aSize
, SurfaceFormat aFormat
) const {
1200 RefPtr
<DrawTargetD2D1
> dt
= new DrawTargetD2D1();
1202 if (!dt
->Init(aSize
, aFormat
)) {
1209 bool DrawTargetD2D1::CanCreateSimilarDrawTarget(const IntSize
& aSize
,
1210 SurfaceFormat aFormat
) const {
1211 RefPtr
<ID2D1DeviceContext
> dc
= Factory::GetD2DDeviceContext();
1215 return (dc
->GetMaximumBitmapSize() >= UINT32(aSize
.width
) &&
1216 dc
->GetMaximumBitmapSize() >= UINT32(aSize
.height
));
1219 RefPtr
<DrawTarget
> DrawTargetD2D1::CreateClippedDrawTarget(
1220 const Rect
& aBounds
, SurfaceFormat aFormat
) {
1221 RefPtr
<DrawTarget
> result
;
1223 if (!aBounds
.IsEmpty()) {
1224 PushClipRect(aBounds
);
1227 D2D1_RECT_F clipRect
;
1229 GetDeviceSpaceClipRect(clipRect
, isAligned
);
1230 IntRect rect
= RoundedOut(ToRect(clipRect
));
1232 RefPtr
<DrawTarget
> dt
= CreateSimilarDrawTarget(rect
.Size(), aFormat
);
1234 result
= gfx::Factory::CreateOffsetDrawTarget(dt
, rect
.TopLeft());
1236 result
->SetTransform(mTransform
);
1239 if (!aBounds
.IsEmpty()) {
1246 already_AddRefed
<GradientStops
> DrawTargetD2D1::CreateGradientStops(
1247 GradientStop
* rawStops
, uint32_t aNumStops
, ExtendMode aExtendMode
) const {
1248 if (aNumStops
== 0) {
1249 gfxWarning() << *this
1250 << ": Failed to create GradientStopCollection with no stops.";
1254 D2D1_GRADIENT_STOP
* stops
= new D2D1_GRADIENT_STOP
[aNumStops
];
1256 for (uint32_t i
= 0; i
< aNumStops
; i
++) {
1257 stops
[i
].position
= rawStops
[i
].offset
;
1258 stops
[i
].color
= D2DColor(rawStops
[i
].color
);
1261 RefPtr
<ID2D1GradientStopCollection1
> stopCollection
;
1263 RefPtr
<ID2D1DeviceContext
> dc
= Factory::GetD2DDeviceContext();
1269 HRESULT hr
= dc
->CreateGradientStopCollection(
1270 stops
, aNumStops
, D2D1_COLOR_SPACE_SRGB
, D2D1_COLOR_SPACE_SRGB
,
1271 D2D1_BUFFER_PRECISION_8BPC_UNORM
, D2DExtend(aExtendMode
, Axis::BOTH
),
1272 D2D1_COLOR_INTERPOLATION_MODE_PREMULTIPLIED
,
1273 getter_AddRefs(stopCollection
));
1277 gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: "
1282 RefPtr
<ID3D11Device
> device
= Factory::GetDirect3D11Device();
1283 return MakeAndAddRef
<GradientStopsD2D
>(stopCollection
, device
);
1286 already_AddRefed
<FilterNode
> DrawTargetD2D1::CreateFilter(FilterType aType
) {
1287 if (!EnsureInitialized()) {
1290 return FilterNodeD2D1::Create(mDC
, aType
);
1293 bool DrawTargetD2D1::Init(ID3D11Texture2D
* aTexture
, SurfaceFormat aFormat
) {
1294 RefPtr
<ID2D1Device
> device
= Factory::GetD2D1Device(&mDeviceSeq
);
1296 gfxCriticalNote
<< "[D2D1.1] Failed to obtain a device for "
1297 "DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
1301 aTexture
->QueryInterface(__uuidof(IDXGISurface
),
1302 (void**)((IDXGISurface
**)getter_AddRefs(mSurface
)));
1304 gfxCriticalError() << "[D2D1.1] Failed to obtain a DXGI surface.";
1310 D3D11_TEXTURE2D_DESC desc
;
1311 aTexture
->GetDesc(&desc
);
1312 mSize
.width
= desc
.Width
;
1313 mSize
.height
= desc
.Height
;
1318 bool DrawTargetD2D1::Init(const IntSize
& aSize
, SurfaceFormat aFormat
) {
1319 RefPtr
<ID2D1Device
> device
= Factory::GetD2D1Device(&mDeviceSeq
);
1321 gfxCriticalNote
<< "[D2D1.1] Failed to obtain a device for "
1322 "DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
1326 if (!CanCreateSimilarDrawTarget(aSize
, aFormat
)) {
1327 // Size unsupported.
1340 uint32_t DrawTargetD2D1::GetByteSize() const {
1341 return mSize
.width
* mSize
.height
* BytesPerPixel(mFormat
);
1344 RefPtr
<ID2D1Factory1
> DrawTargetD2D1::factory() {
1345 StaticMutexAutoLock
lock(Factory::mDeviceLock
);
1347 if (mFactory
|| !NS_IsMainThread()) {
1351 // We don't allow initializing the factory off the main thread.
1352 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1354 RefPtr
<ID2D1Factory
> factory
;
1355 D2D1CreateFactoryFunc createD2DFactory
;
1356 HMODULE d2dModule
= LoadLibraryW(L
"d2d1.dll");
1358 (D2D1CreateFactoryFunc
)GetProcAddress(d2dModule
, "D2D1CreateFactory");
1360 if (!createD2DFactory
) {
1361 gfxWarning() << "Failed to locate D2D1CreateFactory function.";
1365 D2D1_FACTORY_OPTIONS options
;
1367 options
.debugLevel
= D2D1_DEBUG_LEVEL_WARNING
;
1369 options
.debugLevel
= D2D1_DEBUG_LEVEL_NONE
;
1371 // options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
1374 createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED
, __uuidof(ID2D1Factory
),
1375 &options
, getter_AddRefs(factory
));
1377 if (FAILED(hr
) || !factory
) {
1378 gfxCriticalNote
<< "Failed to create a D2D1 content device: " << hexa(hr
);
1382 RefPtr
<ID2D1Factory1
> factory1
;
1383 hr
= factory
->QueryInterface(__uuidof(ID2D1Factory1
),
1384 getter_AddRefs(factory1
));
1385 if (FAILED(hr
) || !factory1
) {
1389 mFactory
= factory1
;
1391 ExtendInputEffectD2D1::Register(mFactory
);
1392 ConicGradientEffectD2D1::Register(mFactory
);
1393 RadialGradientEffectD2D1::Register(mFactory
);
1398 void DrawTargetD2D1::CleanupD2D() {
1399 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1400 Factory::mDeviceLock
.AssertCurrentThreadOwns();
1403 RadialGradientEffectD2D1::Unregister(mFactory
);
1404 ConicGradientEffectD2D1::Unregister(mFactory
);
1405 ExtendInputEffectD2D1::Unregister(mFactory
);
1410 void DrawTargetD2D1::FlushInternal(bool aHasDependencyMutex
/* = false */) {
1411 if (IsDeviceContextValid()) {
1412 if ((mUsedCommandListsSincePurge
>= kPushedLayersBeforePurge
||
1413 mTransformedGlyphsSinceLastPurge
>= kTransformedGlyphsBeforePurge
) &&
1414 mPushedLayers
.size() == 1) {
1415 // It's important to pop all clips as otherwise layers can forget about
1416 // their clip when doing an EndDraw. When we have layers pushed we cannot
1417 // easily pop all underlying clips to delay the purge until we have no
1420 mUsedCommandListsSincePurge
= 0;
1421 mTransformedGlyphsSinceLastPurge
= 0;
1429 Maybe
<StaticMutexAutoLock
> lock
;
1431 if (!aHasDependencyMutex
) {
1432 lock
.emplace(Factory::mDTDependencyLock
);
1435 Factory::mDTDependencyLock
.AssertCurrentThreadOwns();
1436 // We no longer depend on any target.
1437 for (TargetSet::iterator iter
= mDependingOnTargets
.begin();
1438 iter
!= mDependingOnTargets
.end(); iter
++) {
1439 (*iter
)->mDependentTargets
.erase(this);
1441 mDependingOnTargets
.clear();
1444 bool DrawTargetD2D1::EnsureInitialized() {
1445 if (mInitState
!= InitState::Uninitialized
) {
1446 return mInitState
== InitState::Success
;
1450 mInitState
= InitState::Failure
;
1454 RefPtr
<ID2D1Device
> device
= Factory::GetD2D1Device(&mDeviceSeq
);
1456 gfxCriticalNote
<< "[D2D1.1] Failed to obtain a device for "
1457 "DrawTargetD2D1::EnsureInitialized().";
1461 hr
= device
->CreateDeviceContext(
1462 D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS
,
1463 getter_AddRefs(mDC
));
1466 gfxCriticalError() << "[D2D1.1] 2Failed to create a DeviceContext, code: "
1467 << hexa(hr
) << " format " << (int)mFormat
;
1472 if (mDC
->GetMaximumBitmapSize() < UINT32(mSize
.width
) ||
1473 mDC
->GetMaximumBitmapSize() < UINT32(mSize
.height
)) {
1474 // This is 'ok', so don't assert
1475 gfxCriticalNote
<< "[D2D1.1] Attempt to use unsupported surface size "
1480 D2D1_BITMAP_PROPERTIES1 props
;
1483 props
.pixelFormat
= D2DPixelFormat(mFormat
);
1484 props
.colorContext
= nullptr;
1485 props
.bitmapOptions
= D2D1_BITMAP_OPTIONS_TARGET
;
1486 hr
= mDC
->CreateBitmap(D2DIntSize(mSize
), nullptr, 0, props
,
1487 (ID2D1Bitmap1
**)getter_AddRefs(mBitmap
));
1490 gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << mSize
1491 << " Code: " << hexa(hr
) << " format " << (int)mFormat
;
1495 D2D1_BITMAP_PROPERTIES1 props
;
1498 props
.pixelFormat
= D2DPixelFormat(mFormat
);
1499 props
.colorContext
= nullptr;
1500 props
.bitmapOptions
= D2D1_BITMAP_OPTIONS_TARGET
;
1501 hr
= mDC
->CreateBitmapFromDxgiSurface(
1502 mSurface
, props
, (ID2D1Bitmap1
**)getter_AddRefs(mBitmap
));
1506 << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr
)
1507 << " format " << (int)mFormat
;
1512 mDC
->SetTarget(CurrentTarget());
1514 hr
= mDC
->CreateSolidColorBrush(D2D1::ColorF(0, 0),
1515 getter_AddRefs(mSolidColorBrush
));
1518 gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
1524 CurrentLayer().mIsOpaque
= mFormat
== SurfaceFormat::B8G8R8X8
;
1530 mInitState
= InitState::Success
;
1535 void DrawTargetD2D1::MarkChanged() {
1537 MutexAutoLock
lock(*mSnapshotLock
);
1538 if (mSnapshot
->hasOneRef()) {
1539 // Just destroy it, since no-one else knows about it.
1540 mSnapshot
= nullptr;
1542 mSnapshot
->DrawTargetWillChange();
1543 // The snapshot will no longer depend on this target.
1544 MOZ_ASSERT(!mSnapshot
);
1549 StaticMutexAutoLock
lock(Factory::mDTDependencyLock
);
1550 if (mDependentTargets
.size()) {
1551 // Copy mDependentTargets since the Flush()es below will modify it.
1552 TargetSet tmpTargets
= mDependentTargets
;
1553 for (TargetSet::iterator iter
= tmpTargets
.begin();
1554 iter
!= tmpTargets
.end(); iter
++) {
1555 (*iter
)->FlushInternal(true);
1557 // The Flush() should have broken all dependencies on this target.
1558 MOZ_ASSERT(!mDependentTargets
.size());
1563 bool DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp
,
1564 const Pattern
& aPattern
,
1565 bool aClipIsComplex
) {
1566 bool patternSupported
= IsPatternSupportedByD2D(aPattern
, aOp
);
1567 return patternSupported
&& !CurrentLayer().mIsOpaque
&&
1568 D2DSupportsCompositeMode(aOp
) && IsOperatorBoundByMask(aOp
) &&
1572 bool DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp
,
1573 const Pattern
& aPattern
) {
1574 if (!EnsureInitialized()) {
1582 bool patternSupported
= IsPatternSupportedByD2D(aPattern
, aOp
);
1583 if (D2DSupportsPrimitiveBlendMode(aOp
) && patternSupported
) {
1584 // It's important to do this before FlushTransformToDC! As this will cause
1585 // the transform to become dirty.
1587 FlushTransformToDC();
1589 if (aOp
!= CompositionOp::OP_OVER
) {
1590 mDC
->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp
));
1596 HRESULT result
= mDC
->CreateCommandList(getter_AddRefs(mCommandList
));
1597 mDC
->SetTarget(mCommandList
);
1598 mUsedCommandListsSincePurge
++;
1600 // This is where we should have a valid command list. If we don't, something
1601 // is wrong, and it's likely an OOM.
1602 if (!mCommandList
) {
1603 gfxDevCrash(LogReason::InvalidCommandList
)
1604 << "Invalid D2D1.1 command list on creation "
1605 << mUsedCommandListsSincePurge
<< ", " << gfx::hexa(result
);
1610 bool clipIsComplex
= CurrentLayer().mPushedClips
.size() &&
1611 !GetDeviceSpaceClipRect(rect
, isAligned
);
1613 if (ShouldClipTemporarySurfaceDrawing(aOp
, aPattern
, clipIsComplex
)) {
1617 FlushTransformToDC();
1622 void DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp
,
1623 const Pattern
& aPattern
) {
1624 bool patternSupported
= IsPatternSupportedByD2D(aPattern
, aOp
);
1626 if (D2DSupportsPrimitiveBlendMode(aOp
) && patternSupported
) {
1627 if (aOp
!= CompositionOp::OP_OVER
)
1628 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER
);
1634 bool clipIsComplex
= CurrentLayer().mPushedClips
.size() &&
1635 !GetDeviceSpaceClipRect(rect
, isAligned
);
1637 if (ShouldClipTemporarySurfaceDrawing(aOp
, aPattern
, clipIsComplex
)) {
1638 PopClipsFromDC(mDC
);
1641 mDC
->SetTarget(CurrentTarget());
1642 if (!mCommandList
) {
1643 gfxDevCrash(LogReason::InvalidCommandList
)
1644 << "Invalid D21.1 command list on finalize";
1647 mCommandList
->Close();
1649 RefPtr
<ID2D1CommandList
> source
= mCommandList
;
1650 mCommandList
= nullptr;
1652 mDC
->SetTransform(D2D1::IdentityMatrix());
1653 mTransformDirty
= true;
1655 if (patternSupported
) {
1656 if (D2DSupportsCompositeMode(aOp
)) {
1657 RefPtr
<ID2D1Image
> tmpImage
;
1658 if (clipIsComplex
) {
1660 if (!IsOperatorBoundByMask(aOp
)) {
1661 tmpImage
= GetImageForLayerContent();
1664 mDC
->DrawImage(source
, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
1665 D2DCompositionMode(aOp
));
1668 RefPtr
<ID2D1ImageBrush
> brush
;
1669 RefPtr
<ID2D1Geometry
> inverseGeom
= GetInverseClippedGeometry();
1670 mDC
->CreateImageBrush(tmpImage
,
1671 D2D1::ImageBrushProperties(
1672 D2D1::RectF(0, 0, mSize
.width
, mSize
.height
)),
1673 getter_AddRefs(brush
));
1675 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY
);
1676 mDC
->FillGeometry(inverseGeom
, brush
);
1677 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER
);
1682 RefPtr
<ID2D1Effect
> blendEffect
;
1684 mDC
->CreateEffect(CLSID_D2D1Blend
, getter_AddRefs(blendEffect
));
1686 if (FAILED(hr
) || !blendEffect
) {
1687 gfxWarning() << "Failed to create blend effect!";
1691 IntRect
bounds(IntPoint(), mSize
);
1692 RefPtr
<ID2D1Geometry
> geom
;
1693 if (CurrentLayer().mPushedClips
.size() > 0) {
1694 geom
= GetClippedGeometry(&bounds
);
1696 RefPtr
<ID2D1Image
> tmpImage
= GetImageForLayerContent(&bounds
, bool(geom
));
1701 blendEffect
->SetInput(0, tmpImage
);
1702 blendEffect
->SetInput(1, source
);
1703 blendEffect
->SetValue(D2D1_BLEND_PROP_MODE
, D2DBlendMode(aOp
));
1706 RefPtr
<ID2D1Image
> blendOutput
;
1707 blendEffect
->GetOutput(getter_AddRefs(blendOutput
));
1709 RefPtr
<ID2D1ImageBrush
> brush
;
1710 mDC
->CreateImageBrush(
1711 blendOutput
, D2D1::ImageBrushProperties(D2DRect(bounds
)),
1712 D2D1::BrushProperties(
1713 1.0f
, D2D1::Matrix3x2F::Translation(bounds
.x
, bounds
.y
)),
1714 getter_AddRefs(brush
));
1716 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY
);
1717 mDC
->FillGeometry(geom
, brush
);
1718 mDC
->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER
);
1720 mDC
->DrawImage(blendEffect
, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
1721 D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY
);
1724 mComplexBlendsWithListInList
++;
1728 if (aPattern
.GetType() == PatternType::CONIC_GRADIENT
) {
1729 const ConicGradientPattern
* pat
=
1730 static_cast<const ConicGradientPattern
*>(&aPattern
);
1733 pat
->mStops
->GetBackendType() != BackendType::DIRECT2D
) {
1734 // Draw nothing because of no color stops
1737 RefPtr
<ID2D1Effect
> conicGradientEffect
;
1739 HRESULT hr
= mDC
->CreateEffect(CLSID_ConicGradientEffect
,
1740 getter_AddRefs(conicGradientEffect
));
1741 if (FAILED(hr
) || !conicGradientEffect
) {
1742 gfxWarning() << "Failed to create conic gradient effect. Code: "
1749 conicGradientEffect
->SetValue(
1750 CONIC_PROP_STOP_COLLECTION
,
1751 static_cast<const GradientStopsD2D
*>(pat
->mStops
.get())
1753 conicGradientEffect
->SetValue(
1754 CONIC_PROP_CENTER
, D2D1::Vector2F(pat
->mCenter
.x
, pat
->mCenter
.y
));
1755 conicGradientEffect
->SetValue(CONIC_PROP_ANGLE
, pat
->mAngle
);
1756 conicGradientEffect
->SetValue(CONIC_PROP_START_OFFSET
, pat
->mStartOffset
);
1757 conicGradientEffect
->SetValue(CONIC_PROP_END_OFFSET
, pat
->mEndOffset
);
1758 conicGradientEffect
->SetValue(CONIC_PROP_TRANSFORM
,
1759 D2DMatrix(pat
->mMatrix
* mTransform
));
1760 conicGradientEffect
->SetInput(0, source
);
1762 mDC
->DrawImage(conicGradientEffect
,
1763 D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
1764 D2DCompositionMode(aOp
));
1768 MOZ_ASSERT(aPattern
.GetType() == PatternType::RADIAL_GRADIENT
);
1770 const RadialGradientPattern
* pat
=
1771 static_cast<const RadialGradientPattern
*>(&aPattern
);
1772 if (pat
->mCenter1
== pat
->mCenter2
&& pat
->mRadius1
== pat
->mRadius2
) {
1777 if (!pat
->mStops
|| pat
->mStops
->GetBackendType() != BackendType::DIRECT2D
) {
1778 // Draw nothing because of no color stops
1782 RefPtr
<ID2D1Effect
> radialGradientEffect
;
1784 HRESULT hr
= mDC
->CreateEffect(CLSID_RadialGradientEffect
,
1785 getter_AddRefs(radialGradientEffect
));
1786 if (FAILED(hr
) || !radialGradientEffect
) {
1787 gfxWarning() << "Failed to create radial gradient effect. Code: "
1794 radialGradientEffect
->SetValue(
1795 RADIAL_PROP_STOP_COLLECTION
,
1796 static_cast<const GradientStopsD2D
*>(pat
->mStops
.get())->mStopCollection
);
1797 radialGradientEffect
->SetValue(
1798 RADIAL_PROP_CENTER_1
, D2D1::Vector2F(pat
->mCenter1
.x
, pat
->mCenter1
.y
));
1799 radialGradientEffect
->SetValue(
1800 RADIAL_PROP_CENTER_2
, D2D1::Vector2F(pat
->mCenter2
.x
, pat
->mCenter2
.y
));
1801 radialGradientEffect
->SetValue(RADIAL_PROP_RADIUS_1
, pat
->mRadius1
);
1802 radialGradientEffect
->SetValue(RADIAL_PROP_RADIUS_2
, pat
->mRadius2
);
1803 radialGradientEffect
->SetValue(RADIAL_PROP_RADIUS_2
, pat
->mRadius2
);
1804 radialGradientEffect
->SetValue(RADIAL_PROP_TRANSFORM
,
1805 D2DMatrix(pat
->mMatrix
* mTransform
));
1806 radialGradientEffect
->SetInput(0, source
);
1808 mDC
->DrawImage(radialGradientEffect
, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
1809 D2DCompositionMode(aOp
));
1812 void DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1
* aSource
) {
1813 Maybe
<MutexAutoLock
> snapshotLock
;
1814 // We grab the SnapshotLock as well, this guaranteeds aSource->mDrawTarget
1815 // cannot be cleared in between the if statement and the dereference.
1816 if (aSource
->mSnapshotLock
) {
1817 snapshotLock
.emplace(*aSource
->mSnapshotLock
);
1820 StaticMutexAutoLock
lock(Factory::mDTDependencyLock
);
1821 if (aSource
->mDrawTarget
&&
1822 !mDependingOnTargets
.count(aSource
->mDrawTarget
)) {
1823 aSource
->mDrawTarget
->mDependentTargets
.insert(this);
1824 mDependingOnTargets
.insert(aSource
->mDrawTarget
);
1829 static D2D1_RECT_F
IntersectRect(const D2D1_RECT_F
& aRect1
,
1830 const D2D1_RECT_F
& aRect2
) {
1832 result
.left
= std::max(aRect1
.left
, aRect2
.left
);
1833 result
.top
= std::max(aRect1
.top
, aRect2
.top
);
1834 result
.right
= std::min(aRect1
.right
, aRect2
.right
);
1835 result
.bottom
= std::min(aRect1
.bottom
, aRect2
.bottom
);
1837 result
.right
= std::max(result
.right
, result
.left
);
1838 result
.bottom
= std::max(result
.bottom
, result
.top
);
1843 bool DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F
& aClipRect
,
1844 bool& aIsPixelAligned
) {
1845 aIsPixelAligned
= true;
1846 aClipRect
= D2D1::RectF(0, 0, mSize
.width
, mSize
.height
);
1848 if (!CurrentLayer().mPushedClips
.size()) {
1852 for (auto iter
= CurrentLayer().mPushedClips
.begin();
1853 iter
!= CurrentLayer().mPushedClips
.end(); iter
++) {
1854 if (iter
->mGeometry
) {
1857 aClipRect
= IntersectRect(aClipRect
, iter
->mBounds
);
1858 if (!iter
->mIsPixelAligned
) {
1859 aIsPixelAligned
= false;
1865 static const uint32_t sComplexBlendsWithListAllowedInList
= 4;
1867 already_AddRefed
<ID2D1Image
> DrawTargetD2D1::GetImageForLayerContent(
1868 const IntRect
* aBounds
, bool aShouldPreserveContent
) {
1871 IntRect bounds
= aBounds
? *aBounds
: IntRect(IntPoint(), mSize
);
1872 IntSize
size(bounds
.XMost(), bounds
.YMost());
1873 if (!CurrentLayer().mCurrentList
) {
1874 RefPtr
<ID2D1Bitmap
> tmpBitmap
;
1875 HRESULT hr
= mDC
->CreateBitmap(
1876 D2DIntSize(size
), D2D1::BitmapProperties(D2DPixelFormat(mFormat
)),
1877 getter_AddRefs(tmpBitmap
));
1880 CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(size
)))
1881 << "[D2D1.1] 6CreateBitmap failure " << size
<< " Code: " << hexa(hr
)
1882 << " format " << (int)mFormat
;
1883 // If it's a recreate target error, return and handle it elsewhere.
1884 if (hr
== D2DERR_RECREATE_TARGET
) {
1888 // For now, crash in other scenarios; this should happen because tmpBitmap
1889 // is null and CopyFromBitmap call below dereferences it.
1893 D2D1_POINT_2U destOffset
= D2D1::Point2U(bounds
.x
, bounds
.y
);
1894 D2D1_RECT_U srcRect
=
1895 D2D1::RectU(bounds
.x
, bounds
.y
, bounds
.width
, bounds
.height
);
1896 tmpBitmap
->CopyFromBitmap(&destOffset
, mBitmap
, &srcRect
);
1897 return tmpBitmap
.forget();
1899 RefPtr
<ID2D1CommandList
> list
= CurrentLayer().mCurrentList
;
1900 mDC
->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList
));
1901 mDC
->SetTarget(CurrentTarget());
1904 RefPtr
<ID2D1Bitmap1
> tmpBitmap
;
1905 if (mComplexBlendsWithListInList
>= sComplexBlendsWithListAllowedInList
) {
1906 D2D1_BITMAP_PROPERTIES1 props
= D2D1::BitmapProperties1(
1907 D2D1_BITMAP_OPTIONS_TARGET
,
1908 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
,
1909 D2D1_ALPHA_MODE_PREMULTIPLIED
));
1910 mDC
->CreateBitmap(D2DIntSize(size
), nullptr, 0, &props
,
1911 getter_AddRefs(tmpBitmap
));
1912 mDC
->SetTransform(D2D1::IdentityMatrix());
1913 mTransformDirty
= true;
1914 mDC
->SetTarget(tmpBitmap
);
1915 mDC
->DrawImage(list
, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR
,
1916 D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY
);
1917 mDC
->SetTarget(CurrentTarget());
1918 mComplexBlendsWithListInList
= 0;
1921 DCCommandSink
sink(mDC
);
1923 if (aShouldPreserveContent
) {
1924 list
->Stream(&sink
);
1928 return tmpBitmap
.forget();
1931 return list
.forget();
1935 already_AddRefed
<ID2D1Geometry
> DrawTargetD2D1::GetClippedGeometry(
1936 IntRect
* aClipBounds
) {
1937 if (mCurrentClippedGeometry
) {
1938 *aClipBounds
= mCurrentClipBounds
;
1939 RefPtr
<ID2D1Geometry
> clippedGeometry(mCurrentClippedGeometry
);
1940 return clippedGeometry
.forget();
1943 MOZ_ASSERT(CurrentLayer().mPushedClips
.size());
1945 mCurrentClipBounds
= IntRect(IntPoint(0, 0), mSize
);
1947 // if pathGeom is null then pathRect represents the path.
1948 RefPtr
<ID2D1Geometry
> pathGeom
;
1949 D2D1_RECT_F pathRect
;
1950 bool pathRectIsAxisAligned
= false;
1951 auto iter
= CurrentLayer().mPushedClips
.begin();
1953 if (iter
->mGeometry
) {
1954 pathGeom
= GetTransformedGeometry(iter
->mGeometry
, iter
->mTransform
);
1956 pathRect
= iter
->mBounds
;
1957 pathRectIsAxisAligned
= iter
->mIsPixelAligned
;
1961 for (; iter
!= CurrentLayer().mPushedClips
.end(); iter
++) {
1962 // Do nothing but add it to the current clip bounds.
1963 if (!iter
->mGeometry
&& iter
->mIsPixelAligned
) {
1964 mCurrentClipBounds
.IntersectRect(
1966 IntRect(int32_t(iter
->mBounds
.left
), int32_t(iter
->mBounds
.top
),
1967 int32_t(iter
->mBounds
.right
- iter
->mBounds
.left
),
1968 int32_t(iter
->mBounds
.bottom
- iter
->mBounds
.top
)));
1973 if (pathRectIsAxisAligned
) {
1974 mCurrentClipBounds
.IntersectRect(
1976 IntRect(int32_t(pathRect
.left
), int32_t(pathRect
.top
),
1977 int32_t(pathRect
.right
- pathRect
.left
),
1978 int32_t(pathRect
.bottom
- pathRect
.top
)));
1980 if (iter
->mGeometry
) {
1981 // See if pathRect needs to go into the path geometry.
1982 if (!pathRectIsAxisAligned
) {
1983 pathGeom
= ConvertRectToGeometry(pathRect
);
1985 pathGeom
= GetTransformedGeometry(iter
->mGeometry
, iter
->mTransform
);
1988 pathRect
= IntersectRect(pathRect
, iter
->mBounds
);
1989 pathRectIsAxisAligned
= false;
1994 RefPtr
<ID2D1PathGeometry
> newGeom
;
1995 factory()->CreatePathGeometry(getter_AddRefs(newGeom
));
1997 RefPtr
<ID2D1GeometrySink
> currentSink
;
1998 newGeom
->Open(getter_AddRefs(currentSink
));
2000 if (iter
->mGeometry
) {
2001 pathGeom
->CombineWithGeometry(iter
->mGeometry
,
2002 D2D1_COMBINE_MODE_INTERSECT
,
2003 iter
->mTransform
, currentSink
);
2005 RefPtr
<ID2D1Geometry
> rectGeom
= ConvertRectToGeometry(iter
->mBounds
);
2006 pathGeom
->CombineWithGeometry(rectGeom
, D2D1_COMBINE_MODE_INTERSECT
,
2007 D2D1::IdentityMatrix(), currentSink
);
2010 currentSink
->Close();
2012 pathGeom
= newGeom
.forget();
2015 // For now we need mCurrentClippedGeometry to always be non-nullptr. This
2016 // method might seem a little strange but it is just fine, if pathGeom is
2017 // nullptr pathRect will always still contain 1 clip unaccounted for
2018 // regardless of mCurrentClipBounds.
2020 pathGeom
= ConvertRectToGeometry(pathRect
);
2022 mCurrentClippedGeometry
= pathGeom
.forget();
2023 *aClipBounds
= mCurrentClipBounds
;
2024 RefPtr
<ID2D1Geometry
> clippedGeometry(mCurrentClippedGeometry
);
2025 return clippedGeometry
.forget();
2028 already_AddRefed
<ID2D1Geometry
> DrawTargetD2D1::GetInverseClippedGeometry() {
2030 RefPtr
<ID2D1Geometry
> geom
= GetClippedGeometry(&bounds
);
2031 RefPtr
<ID2D1RectangleGeometry
> rectGeom
;
2032 RefPtr
<ID2D1PathGeometry
> inverseGeom
;
2034 factory()->CreateRectangleGeometry(
2035 D2D1::RectF(0, 0, mSize
.width
, mSize
.height
), getter_AddRefs(rectGeom
));
2036 factory()->CreatePathGeometry(getter_AddRefs(inverseGeom
));
2037 RefPtr
<ID2D1GeometrySink
> sink
;
2038 inverseGeom
->Open(getter_AddRefs(sink
));
2039 rectGeom
->CombineWithGeometry(geom
, D2D1_COMBINE_MODE_EXCLUDE
,
2040 D2D1::IdentityMatrix(), sink
);
2043 return inverseGeom
.forget();
2046 void DrawTargetD2D1::PopAllClips() {
2047 if (CurrentLayer().mClipsArePushed
) {
2048 PopClipsFromDC(mDC
);
2050 CurrentLayer().mClipsArePushed
= false;
2054 void DrawTargetD2D1::PushAllClips() {
2055 if (!CurrentLayer().mClipsArePushed
) {
2058 CurrentLayer().mClipsArePushed
= true;
2062 void DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext
* aDC
,
2063 bool aForceIgnoreAlpha
,
2064 const D2D1_RECT_F
& aMaxRect
) {
2065 mDC
->SetTransform(D2D1::IdentityMatrix());
2066 mTransformDirty
= true;
2068 for (auto iter
= CurrentLayer().mPushedClips
.begin();
2069 iter
!= CurrentLayer().mPushedClips
.end(); iter
++) {
2070 if (iter
->mGeometry
) {
2071 PushD2DLayer(aDC
, iter
->mGeometry
, iter
->mTransform
,
2072 iter
->mIsPixelAligned
, aForceIgnoreAlpha
, aMaxRect
);
2074 mDC
->PushAxisAlignedClip(iter
->mBounds
,
2075 iter
->mIsPixelAligned
2076 ? D2D1_ANTIALIAS_MODE_ALIASED
2077 : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
);
2082 void DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext
* aDC
) {
2083 for (int i
= CurrentLayer().mPushedClips
.size() - 1; i
>= 0; i
--) {
2084 if (CurrentLayer().mPushedClips
[i
].mGeometry
) {
2087 aDC
->PopAxisAlignedClip();
2092 already_AddRefed
<ID2D1Brush
> DrawTargetD2D1::CreateTransparentBlackBrush() {
2093 return GetSolidColorBrush(D2D1::ColorF(0, 0));
2096 already_AddRefed
<ID2D1SolidColorBrush
> DrawTargetD2D1::GetSolidColorBrush(
2097 const D2D_COLOR_F
& aColor
) {
2098 RefPtr
<ID2D1SolidColorBrush
> brush
= mSolidColorBrush
;
2099 brush
->SetColor(aColor
);
2100 return brush
.forget();
2103 already_AddRefed
<ID2D1Brush
> DrawTargetD2D1::CreateBrushForPattern(
2104 const Pattern
& aPattern
, const DrawOptions
& aOptions
) {
2105 if (!IsPatternSupportedByD2D(aPattern
) ||
2106 aOptions
.mCompositionOp
== CompositionOp::OP_CLEAR
) {
2107 return GetSolidColorBrush(D2D1::ColorF(1.0f
, 1.0f
, 1.0f
, 1.0f
));
2110 if (aPattern
.GetType() == PatternType::COLOR
) {
2111 DeviceColor color
= static_cast<const ColorPattern
*>(&aPattern
)->mColor
;
2112 return GetSolidColorBrush(
2113 D2D1::ColorF(color
.r
, color
.g
, color
.b
, color
.a
* aOptions
.mAlpha
));
2115 if (aPattern
.GetType() == PatternType::LINEAR_GRADIENT
) {
2116 RefPtr
<ID2D1LinearGradientBrush
> gradBrush
;
2117 const LinearGradientPattern
* pat
=
2118 static_cast<const LinearGradientPattern
*>(&aPattern
);
2121 pat
->mStops
->GetBackendType() != BackendType::DIRECT2D
) {
2122 gfxDebug() << "No stops specified for gradient pattern.";
2123 return CreateTransparentBlackBrush();
2126 if (pat
->mBegin
== pat
->mEnd
) {
2127 return CreateTransparentBlackBrush();
2130 GradientStopsD2D
* stops
= static_cast<GradientStopsD2D
*>(pat
->mStops
.get());
2132 mDC
->CreateLinearGradientBrush(
2133 D2D1::LinearGradientBrushProperties(D2DPoint(pat
->mBegin
),
2134 D2DPoint(pat
->mEnd
)),
2135 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(pat
->mMatrix
)),
2136 stops
->mStopCollection
, getter_AddRefs(gradBrush
));
2139 gfxWarning() << "Couldn't create gradient brush.";
2140 return CreateTransparentBlackBrush();
2143 return gradBrush
.forget();
2145 if (aPattern
.GetType() == PatternType::RADIAL_GRADIENT
) {
2146 RefPtr
<ID2D1RadialGradientBrush
> gradBrush
;
2147 const RadialGradientPattern
* pat
=
2148 static_cast<const RadialGradientPattern
*>(&aPattern
);
2151 pat
->mStops
->GetBackendType() != BackendType::DIRECT2D
) {
2152 gfxDebug() << "No stops specified for gradient pattern.";
2153 return CreateTransparentBlackBrush();
2156 if (pat
->mCenter1
== pat
->mCenter2
&& pat
->mRadius1
== pat
->mRadius2
) {
2157 return CreateTransparentBlackBrush();
2160 GradientStopsD2D
* stops
= static_cast<GradientStopsD2D
*>(pat
->mStops
.get());
2162 // This will not be a complex radial gradient brush.
2163 mDC
->CreateRadialGradientBrush(
2164 D2D1::RadialGradientBrushProperties(
2165 D2DPoint(pat
->mCenter2
), D2DPoint(pat
->mCenter1
- pat
->mCenter2
),
2166 pat
->mRadius2
, pat
->mRadius2
),
2167 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(pat
->mMatrix
)),
2168 stops
->mStopCollection
, getter_AddRefs(gradBrush
));
2171 gfxWarning() << "Couldn't create gradient brush.";
2172 return CreateTransparentBlackBrush();
2175 return gradBrush
.forget();
2177 if (aPattern
.GetType() == PatternType::SURFACE
) {
2178 const SurfacePattern
* pat
= static_cast<const SurfacePattern
*>(&aPattern
);
2180 if (!pat
->mSurface
) {
2181 gfxDebug() << "No source surface specified for surface pattern";
2182 return CreateTransparentBlackBrush();
2185 D2D1_RECT_F samplingBounds
;
2186 Matrix mat
= pat
->mMatrix
;
2188 MOZ_ASSERT(pat
->mSurface
->IsValid());
2190 RefPtr
<SourceSurface
> surf
= pat
->mSurface
;
2192 RefPtr
<ID2D1Image
> image
= GetImageForSurface(
2193 surf
, mat
, pat
->mExtendMode
,
2194 !pat
->mSamplingRect
.IsEmpty() ? &pat
->mSamplingRect
: nullptr);
2197 return CreateTransparentBlackBrush();
2200 if (surf
->GetFormat() == SurfaceFormat::A8
) {
2201 // See bug 1251431, at least FillOpacityMask does not appear to allow a
2202 // source bitmapbrush with source format A8. This creates a BGRA surface
2203 // with the same alpha values that the A8 surface has.
2204 RefPtr
<ID2D1Bitmap
> bitmap
;
2205 HRESULT hr
= image
->QueryInterface((ID2D1Bitmap
**)getter_AddRefs(bitmap
));
2206 if (SUCCEEDED(hr
) && bitmap
) {
2207 RefPtr
<ID2D1Image
> oldTarget
;
2208 RefPtr
<ID2D1Bitmap1
> tmpBitmap
;
2209 mDC
->CreateBitmap(D2D1::SizeU(pat
->mSurface
->GetSize().width
,
2210 pat
->mSurface
->GetSize().height
),
2212 D2D1::BitmapProperties1(
2213 D2D1_BITMAP_OPTIONS_TARGET
,
2214 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM
,
2215 D2D1_ALPHA_MODE_PREMULTIPLIED
)),
2216 getter_AddRefs(tmpBitmap
));
2219 return CreateTransparentBlackBrush();
2222 mDC
->GetTarget(getter_AddRefs(oldTarget
));
2223 mDC
->SetTarget(tmpBitmap
);
2225 RefPtr
<ID2D1SolidColorBrush
> brush
;
2226 mDC
->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White
),
2227 getter_AddRefs(brush
));
2228 mDC
->FillOpacityMask(bitmap
, brush
);
2229 mDC
->SetTarget(oldTarget
);
2234 if (pat
->mSamplingRect
.IsEmpty()) {
2235 RefPtr
<ID2D1Bitmap
> bitmap
;
2236 HRESULT hr
= image
->QueryInterface((ID2D1Bitmap
**)getter_AddRefs(bitmap
));
2237 if (SUCCEEDED(hr
) && bitmap
) {
2239 * Create the brush with the proper repeat modes.
2241 RefPtr
<ID2D1BitmapBrush
> bitmapBrush
;
2242 D2D1_EXTEND_MODE xRepeat
= D2DExtend(pat
->mExtendMode
, Axis::X_AXIS
);
2243 D2D1_EXTEND_MODE yRepeat
= D2DExtend(pat
->mExtendMode
, Axis::Y_AXIS
);
2245 mDC
->CreateBitmapBrush(
2247 D2D1::BitmapBrushProperties(xRepeat
, yRepeat
,
2248 D2DFilter(pat
->mSamplingFilter
)),
2249 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(mat
)),
2250 getter_AddRefs(bitmapBrush
));
2252 gfxWarning() << "Couldn't create bitmap brush!";
2253 return CreateTransparentBlackBrush();
2255 return bitmapBrush
.forget();
2259 RefPtr
<ID2D1ImageBrush
> imageBrush
;
2260 if (pat
->mSamplingRect
.IsEmpty()) {
2261 samplingBounds
= D2D1::RectF(0, 0, Float(pat
->mSurface
->GetSize().width
),
2262 Float(pat
->mSurface
->GetSize().height
));
2263 } else if (surf
->GetType() == SurfaceType::D2D1_1_IMAGE
) {
2264 samplingBounds
= D2DRect(pat
->mSamplingRect
);
2265 mat
.PreTranslate(pat
->mSamplingRect
.X(), pat
->mSamplingRect
.Y());
2267 // We will do a partial upload of the sampling restricted area from
2268 // GetImageForSurface.
2269 samplingBounds
= D2D1::RectF(0, 0, pat
->mSamplingRect
.Width(),
2270 pat
->mSamplingRect
.Height());
2273 D2D1_EXTEND_MODE xRepeat
= D2DExtend(pat
->mExtendMode
, Axis::X_AXIS
);
2274 D2D1_EXTEND_MODE yRepeat
= D2DExtend(pat
->mExtendMode
, Axis::Y_AXIS
);
2276 mDC
->CreateImageBrush(
2278 D2D1::ImageBrushProperties(samplingBounds
, xRepeat
, yRepeat
,
2279 D2DInterpolationMode(pat
->mSamplingFilter
)),
2280 D2D1::BrushProperties(aOptions
.mAlpha
, D2DMatrix(mat
)),
2281 getter_AddRefs(imageBrush
));
2284 gfxWarning() << "Couldn't create image brush!";
2285 return CreateTransparentBlackBrush();
2288 return imageBrush
.forget();
2291 gfxWarning() << "Invalid pattern type detected.";
2292 return CreateTransparentBlackBrush();
2295 already_AddRefed
<ID2D1Image
> DrawTargetD2D1::GetImageForSurface(
2296 SourceSurface
* aSurface
, Matrix
& aSourceTransform
, ExtendMode aExtendMode
,
2297 const IntRect
* aSourceRect
, bool aUserSpace
) {
2298 RefPtr
<ID2D1Image
> image
;
2299 RefPtr
<SourceSurface
> surface
= aSurface
->GetUnderlyingSurface();
2305 switch (surface
->GetType()) {
2306 case SurfaceType::D2D1_1_IMAGE
: {
2307 SourceSurfaceD2D1
* surf
= static_cast<SourceSurfaceD2D1
*>(surface
.get());
2308 image
= surf
->GetImage();
2309 AddDependencyOnSource(surf
);
2312 RefPtr
<DataSourceSurface
> dataSurf
= surface
->GetDataSurface();
2314 gfxWarning() << "Invalid surface type.";
2317 Matrix transform
= aUserSpace
? mTransform
: Matrix();
2318 return CreatePartialBitmapForSurface(dataSurf
, transform
, mSize
,
2319 aExtendMode
, aSourceTransform
, mDC
,
2324 return image
.forget();
2327 already_AddRefed
<SourceSurface
> DrawTargetD2D1::OptimizeSourceSurface(
2328 SourceSurface
* aSurface
) const {
2329 if (aSurface
->GetType() == SurfaceType::D2D1_1_IMAGE
) {
2330 RefPtr
<SourceSurface
> surface(aSurface
);
2331 return surface
.forget();
2334 RefPtr
<ID2D1DeviceContext
> dc
= Factory::GetD2DDeviceContext();
2339 RefPtr
<DataSourceSurface
> data
= aSurface
->GetDataSurface();
2341 std::optional
<SurfaceFormat
> convertTo
;
2342 switch (data
->GetFormat()) {
2343 case gfx::SurfaceFormat::R8G8B8X8
:
2344 convertTo
= SurfaceFormat::B8G8R8X8
;
2346 case gfx::SurfaceFormat::R8G8B8A8
:
2347 convertTo
= SurfaceFormat::B8G8R8X8
;
2354 const auto size
= data
->GetSize();
2355 const RefPtr
<DrawTarget
> dt
=
2356 Factory::CreateDrawTarget(BackendType::SKIA
, size
, *convertTo
);
2360 dt
->CopySurface(data
, {{}, size
}, {});
2362 const RefPtr
<SourceSurface
> snapshot
= dt
->Snapshot();
2363 data
= snapshot
->GetDataSurface();
2366 RefPtr
<ID2D1Bitmap1
> bitmap
;
2368 DataSourceSurface::ScopedMap
map(data
, DataSourceSurface::READ
);
2369 if (MOZ2D_WARN_IF(!map
.IsMapped())) {
2373 HRESULT hr
= dc
->CreateBitmap(
2374 D2DIntSize(data
->GetSize()), map
.GetData(), map
.GetStride(),
2375 D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE
,
2376 D2DPixelFormat(data
->GetFormat())),
2377 getter_AddRefs(bitmap
));
2380 gfxCriticalError(CriticalLog::DefaultOptions(
2381 Factory::ReasonableSurfaceSize(data
->GetSize())))
2382 << "[D2D1.1] 4CreateBitmap failure " << data
->GetSize()
2383 << " Code: " << hexa(hr
) << " format " << (int)data
->GetFormat();
2388 return data
.forget();
2391 return MakeAndAddRef
<SourceSurfaceD2D1
>(bitmap
.get(), dc
.get(),
2392 data
->GetFormat(), data
->GetSize());
2395 void DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext
* aDC
,
2396 ID2D1Geometry
* aGeometry
,
2397 const D2D1_MATRIX_3X2_F
& aTransform
,
2398 bool aPixelAligned
, bool aForceIgnoreAlpha
,
2399 const D2D1_RECT_F
& aMaxRect
) {
2400 D2D1_LAYER_OPTIONS1 options
= D2D1_LAYER_OPTIONS1_NONE
;
2402 if (CurrentLayer().mIsOpaque
|| aForceIgnoreAlpha
) {
2403 options
= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA
|
2404 D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND
;
2407 D2D1_ANTIALIAS_MODE antialias
= aPixelAligned
2408 ? D2D1_ANTIALIAS_MODE_ALIASED
2409 : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE
;
2411 mDC
->PushLayer(D2D1::LayerParameters1(aMaxRect
, aGeometry
, antialias
,
2412 aTransform
, 1.0, nullptr, options
),
2416 bool DrawTargetD2D1::IsDeviceContextValid() const {
2418 return mDC
&& Factory::GetD2D1Device(&seqNo
) && seqNo
== mDeviceSeq
;
2422 } // namespace mozilla