no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / gfx / 2d / DrawTargetD2D1.cpp
blobe0bdd4d0553b91b8666f7385a3154393cd9ad58d
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/. */
7 #include <optional>
9 #include <initguid.h>
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);
30 namespace mozilla {
31 namespace gfx {
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,
39 0, 0, 0, 0, 0, 0);
41 RefPtr<ID2D1Factory1> D2DFactory() { return DrawTargetD2D1::factory(); }
43 DrawTargetD2D1::DrawTargetD2D1()
44 : mPushedLayers(1),
45 mSnapshotLock(std::make_shared<Mutex>("DrawTargetD2D1::mSnapshotLock")),
46 mUsedCommandListsSincePurge(0),
47 mTransformedGlyphsSinceLastPurge(0),
48 mComplexBlendsWithListInList(0),
49 mDeviceSeq(0),
50 mInitState(InitState::Uninitialized) {}
52 DrawTargetD2D1::~DrawTargetD2D1() {
53 PopAllClips();
55 if (mSnapshot) {
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.
71 mDC->EndDraw();
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();
81 iter++) {
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()) {
94 return false;
96 if (NS_IsMainThread()) {
97 // Uninitialized DTs are considered valid.
98 return mInitState != InitState::Failure;
99 } else {
100 return const_cast<DrawTargetD2D1*>(this)->EnsureInitialized();
104 already_AddRefed<SourceSurface> DrawTargetD2D1::Snapshot() {
105 if (!EnsureInitialized()) {
106 return nullptr;
109 MutexAutoLock lock(*mSnapshotLock);
110 if (mSnapshot) {
111 RefPtr<SourceSurface> snapshot(mSnapshot);
112 return snapshot.forget();
114 PopAllClips();
116 Flush();
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()) {
126 return true;
129 HRESULT hr = mDC->CreateEffect(CLSID_D2D1ColorMatrix,
130 getter_AddRefs(mLuminanceEffect));
131 if (FAILED(hr)) {
132 gfxCriticalError() << "Failed to create luminance effect. Code: "
133 << hexa(hr);
134 return false;
137 mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX,
138 kLuminanceMatrix);
139 mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_ALPHA_MODE,
140 D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT);
141 return true;
144 already_AddRefed<SourceSurface> DrawTargetD2D1::IntoLuminanceSource(
145 LuminanceType aLuminanceType, float aOpacity) {
146 if (!EnsureInitialized()) {
147 return nullptr;
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);
161 Flush();
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));
196 return true;
198 return false;
201 void DrawTargetD2D1::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
202 const Rect& aSource,
203 const DrawSurfaceOptions& aSurfOptions,
204 const DrawOptions& aOptions) {
205 if (MaybeClearRect(aOptions.mCompositionOp, aDest)) {
206 return;
209 if (!PrepareForDrawing(aOptions.mCompositionOp,
210 ColorPattern(DeviceColor()))) {
211 return;
214 Rect source = aSource - aSurface->GetRect().TopLeft();
216 D2D1_RECT_F samplingBounds;
218 if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) {
219 samplingBounds = D2DRect(source);
220 } else {
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
231 // it to be.
232 Matrix transform;
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);
240 if (!image) {
241 gfxWarning() << *this << ": Unable to get D2D image for surface.";
242 return;
245 RefPtr<ID2D1Bitmap> bitmap;
246 HRESULT hr = E_FAIL;
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));
257 } else {
258 // This has issues ignoring the alpha channel on windows 7 with images
259 // marked opaque.
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(
266 image,
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.";
283 return;
286 if (MaybeClearRect(aOptions.mCompositionOp,
287 Rect(aDestPoint, aSourceRect.Size()))) {
288 return;
291 if (!PrepareForDrawing(aOptions.mCompositionOp,
292 ColorPattern(DeviceColor()))) {
293 return;
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));
304 } else {
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),
318 imageBrush);
321 FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(DeviceColor()));
324 void DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface* aSurface,
325 const Point& aDest,
326 const ShadowOptions& aShadow,
327 CompositionOp aOperator) {
328 if (!EnsureInitialized()) {
329 return;
332 if (MaybeClearRect(aOperator, Rect(aDest, Size(aSurface->GetSize())))) {
333 return;
336 MarkChanged();
338 Matrix mat;
339 RefPtr<ID2D1Image> image =
340 GetImageForSurface(aSurface, mat, ExtendMode::CLAMP, nullptr, false);
342 if (!image) {
343 gfxWarning() << "Couldn't get image for surface.";
344 return;
347 if (!mat.IsIdentity()) {
348 gfxDebug() << *this
349 << ": At this point complex partial uploads are not supported "
350 "for Shadow surfaces.";
351 return;
354 if (!PrepareForDrawing(aOperator, ColorPattern(aShadow.mColor))) {
355 return;
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,
369 aShadow.mSigma);
370 shadowEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE,
371 D2D1_BORDER_MODE_HARD);
372 } else {
373 shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION,
374 aShadow.mSigma);
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);
384 } else {
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()) {
399 return;
402 if (aRect.IsEmpty()) {
403 // Nothing to be done.
404 return;
407 MarkChanged();
409 PopAllClips();
411 PushClipRect(aRect);
413 if (mTransformDirty || !mTransform.IsIdentity()) {
414 mDC->SetTransform(D2D1::IdentityMatrix());
415 mTransformDirty = true;
418 D2D1_RECT_F clipRect;
419 bool isPixelAligned;
420 if (mTransform.IsRectilinear() &&
421 GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
422 mDC->PushAxisAlignedClip(clipRect, isPixelAligned
423 ? D2D1_ANTIALIAS_MODE_ALIASED
424 : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
425 mDC->Clear();
426 mDC->PopAxisAlignedClip();
428 PopClip();
429 return;
432 RefPtr<ID2D1CommandList> list;
433 mUsedCommandListsSincePurge++;
434 mDC->CreateCommandList(getter_AddRefs(list));
435 mDC->SetTarget(list);
437 IntRect addClipRect;
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());
451 list->Close();
453 mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
454 D2D1_COMPOSITE_MODE_DESTINATION_OUT);
456 PopClip();
458 return;
461 void DrawTargetD2D1::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
462 Point aOffset, const DrawOptions& aOptions) {
463 if (!EnsureInitialized()) {
464 return;
466 MarkChanged();
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());
475 aOffset.x = mat._31;
476 aOffset.y = mat._32;
478 if (!image) {
479 gfxWarning() << "Failed to get image for surface.";
480 return;
483 if (!PrepareForDrawing(aOptions.mCompositionOp, aSource)) {
484 return;
487 IntSize size =
488 IntSize::Truncate(aMask->GetSize().width, aMask->GetSize().height);
489 Rect dest =
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(
503 image,
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));
511 mDC->PushLayer(
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),
517 nullptr);
519 mDC->FillRectangle(D2DRect(dest), source);
520 mDC->PopLayer();
522 FinalizeDrawing(aOptions.mCompositionOp, aSource);
523 return;
524 } else {
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()) {
552 return;
554 MarkChanged();
556 PopAllClips();
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);
566 if (!image) {
567 gfxWarning() << "Couldn't get image for surface.";
568 return;
571 if (mat.HasNonIntegerTranslation()) {
572 gfxDebug() << *this
573 << ": At this point scaled partial uploads are not supported "
574 "for CopySurface.";
575 return;
578 IntRect sourceRect = aSourceRect;
579 sourceRect.SetLeftEdge(sourceRect.X() + (aDestination.x - aSourceRect.X()) -
580 mat._31);
581 sourceRect.SetTopEdge(sourceRect.Y() + (aDestination.y - aSourceRect.Y()) -
582 mat._32);
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);
596 return;
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,
609 D2DRect(srcRect));
610 mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
611 return;
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)) {
623 return;
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)) {
642 return;
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)) {
657 return;
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,
667 strokeStyle);
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)) {
677 return;
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)) {
696 return;
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)) {
715 return;
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.";
733 return;
735 const PathD2D* d2dPath = static_cast<const PathD2D*>(path);
737 if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
738 return;
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,
748 strokeStyle);
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.";
758 return;
760 const PathD2D* d2dPath = static_cast<const PathD2D*>(path);
762 if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
763 return;
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.";
780 return;
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)) {
796 return;
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;
808 switch (aaMode) {
809 case AntialiasMode::NONE:
810 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
811 break;
812 case AntialiasMode::GRAY:
813 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
814 break;
815 case AntialiasMode::SUBPIXEL:
816 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
817 break;
818 default:
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) {
830 // According to
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) {
845 D2D1_RECT_F rect;
846 bool isAligned;
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
853 // subpixel AA.
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);
862 PopAllClips();
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);
878 sink->Close();
880 mDC->PushLayer(
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),
886 nullptr);
889 PushClipsToDC(mDC, true, rect);
890 mDC->SetTransform(D2DMatrix(mTransform));
894 if (brush) {
895 mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
898 if (mTransform.HasNonTranslation()) {
899 mTransformedGlyphsSinceLastPurge += aBuffer.mNumGlyphs;
902 if (needsRepushedLayers) {
903 PopClipsFromDC(mDC);
905 if (!mTransform.IsRectilinear()) {
906 mDC->PopLayer();
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)) {
916 return;
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),
924 nullptr);
926 Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
927 Matrix mat = mTransform;
928 mat.Invert();
930 mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
932 mDC->PopLayer();
934 FinalizeDrawing(aOptions.mCompositionOp, aSource);
937 void DrawTargetD2D1::PushClipGeometry(ID2D1Geometry* aGeometry,
938 const D2D1_MATRIX_3X2_F& aTransform,
939 bool aPixelAligned) {
940 mCurrentClippedGeometry = nullptr;
942 PushedClip clip;
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.";
965 return;
967 if (!EnsureInitialized()) {
968 return;
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()) {
978 return;
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.
983 // See remarks:
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;
991 PushedClip clip;
992 Rect rect = mTransform.TransformBounds(aRect);
993 IntRect intRect;
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,
1012 uint32_t aCount) {
1013 if (!EnsureInitialized()) {
1014 return;
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);
1031 sink->Close();
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()) {
1040 return;
1042 mCurrentClippedGeometry = nullptr;
1043 if (CurrentLayer().mPushedClips.empty()) {
1044 gfxDevCrash(LogReason::UnbalancedClipStack)
1045 << "DrawTargetD2D1::PopClip: No clip to pop.";
1046 return;
1049 if (CurrentLayer().mClipsArePushed) {
1050 if (CurrentLayer().mPushedClips.back().mGeometry) {
1051 mDC->PopLayer();
1052 } else {
1053 mDC->PopAxisAlignedClip();
1056 CurrentLayer().mPushedClips.pop_back();
1059 bool DrawTargetD2D1::RemoveAllClips() {
1060 if (!EnsureInitialized()) {
1061 return false;
1063 mCurrentClippedGeometry = nullptr;
1064 while (!CurrentLayer().mPushedClips.empty()) {
1065 PopClip();
1067 return true;
1070 void DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity,
1071 SourceSurface* aMask,
1072 const Matrix& aMaskTransform,
1073 const IntRect& aBounds, bool aCopyBackground) {
1074 if (!EnsureInitialized()) {
1075 return;
1077 D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
1079 if (aOpaque) {
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;
1090 if (aMask) {
1091 RefPtr<ID2D1Image> image =
1092 GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
1093 mDC->SetTransform(D2D1::IdentityMatrix());
1094 mTransformDirty = true;
1096 maskTransform =
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;
1101 if (image) {
1102 IntSize maskSize = aMask->GetSize();
1103 HRESULT hr = mDC->CreateImageBrush(
1104 image,
1105 D2D1::ImageBrushProperties(
1106 D2D1::RectF(0, 0, maskSize.width, maskSize.height)),
1107 D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)),
1108 getter_AddRefs(mask));
1109 if (FAILED(hr)) {
1110 gfxWarning() << "[D2D1.1] Failed to create a ImageBrush, code: "
1111 << hexa(hr);
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));
1119 sink->Close();
1120 } else {
1121 gfxCriticalError() << "Failed to get image for mask surface!";
1125 PushAllClips();
1127 mDC->PushLayer(D2D1::LayerParameters1(
1128 D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED,
1129 D2DMatrix(maskTransform), aOpacity, mask, options),
1130 nullptr);
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) {
1150 return;
1152 RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
1153 mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
1155 mPushedLayers.pop_back();
1156 mDC->SetTarget(CurrentTarget());
1158 list->Close();
1159 mDC->SetTransform(D2D1::IdentityMatrix());
1160 mTransformDirty = true;
1162 DCCommandSink sink(mDC);
1163 list->Stream(&sink);
1165 mComplexBlendsWithListInList = 0;
1167 mDC->PopLayer();
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();
1176 if (!dc) {
1177 return nullptr;
1180 HRESULT hr =
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) {
1187 gfxCriticalError(
1188 CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
1189 << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr)
1190 << " format " << (int)aFormat;
1191 return nullptr;
1194 return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), dc.get(), aFormat,
1195 aSize);
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)) {
1203 return nullptr;
1206 return dt.forget();
1209 bool DrawTargetD2D1::CanCreateSimilarDrawTarget(const IntSize& aSize,
1210 SurfaceFormat aFormat) const {
1211 RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
1212 if (!dc) {
1213 return false;
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;
1228 bool isAligned;
1229 GetDeviceSpaceClipRect(clipRect, isAligned);
1230 IntRect rect = RoundedOut(ToRect(clipRect));
1232 RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(rect.Size(), aFormat);
1233 if (dt) {
1234 result = gfx::Factory::CreateOffsetDrawTarget(dt, rect.TopLeft());
1235 if (result) {
1236 result->SetTransform(mTransform);
1239 if (!aBounds.IsEmpty()) {
1240 PopClip();
1243 return result;
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.";
1251 return nullptr;
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();
1265 if (!dc) {
1266 return nullptr;
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));
1274 delete[] stops;
1276 if (FAILED(hr)) {
1277 gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: "
1278 << hexa(hr);
1279 return nullptr;
1282 RefPtr<ID3D11Device> device = Factory::GetDirect3D11Device();
1283 return MakeAndAddRef<GradientStopsD2D>(stopCollection, device);
1286 already_AddRefed<FilterNode> DrawTargetD2D1::CreateFilter(FilterType aType) {
1287 if (!EnsureInitialized()) {
1288 return nullptr;
1290 return FilterNodeD2D1::Create(mDC, aType);
1293 bool DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) {
1294 RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1295 if (!device) {
1296 gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1297 "DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
1298 return false;
1301 aTexture->QueryInterface(__uuidof(IDXGISurface),
1302 (void**)((IDXGISurface**)getter_AddRefs(mSurface)));
1303 if (!mSurface) {
1304 gfxCriticalError() << "[D2D1.1] Failed to obtain a DXGI surface.";
1305 return false;
1308 mFormat = aFormat;
1310 D3D11_TEXTURE2D_DESC desc;
1311 aTexture->GetDesc(&desc);
1312 mSize.width = desc.Width;
1313 mSize.height = desc.Height;
1315 return true;
1318 bool DrawTargetD2D1::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1319 RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1320 if (!device) {
1321 gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1322 "DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
1323 return false;
1326 if (!CanCreateSimilarDrawTarget(aSize, aFormat)) {
1327 // Size unsupported.
1328 return false;
1331 mFormat = aFormat;
1332 mSize = aSize;
1334 return true;
1338 * Private helpers.
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()) {
1348 return mFactory;
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");
1357 createD2DFactory =
1358 (D2D1CreateFactoryFunc)GetProcAddress(d2dModule, "D2D1CreateFactory");
1360 if (!createD2DFactory) {
1361 gfxWarning() << "Failed to locate D2D1CreateFactory function.";
1362 return nullptr;
1365 D2D1_FACTORY_OPTIONS options;
1366 #ifdef _DEBUG
1367 options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
1368 #else
1369 options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
1370 #endif
1371 // options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
1373 HRESULT hr =
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);
1379 return nullptr;
1382 RefPtr<ID2D1Factory1> factory1;
1383 hr = factory->QueryInterface(__uuidof(ID2D1Factory1),
1384 getter_AddRefs(factory1));
1385 if (FAILED(hr) || !factory1) {
1386 return nullptr;
1389 mFactory = factory1;
1391 ExtendInputEffectD2D1::Register(mFactory);
1392 ConicGradientEffectD2D1::Register(mFactory);
1393 RadialGradientEffectD2D1::Register(mFactory);
1395 return mFactory;
1398 void DrawTargetD2D1::CleanupD2D() {
1399 MOZ_RELEASE_ASSERT(NS_IsMainThread());
1400 Factory::mDeviceLock.AssertCurrentThreadOwns();
1402 if (mFactory) {
1403 RadialGradientEffectD2D1::Unregister(mFactory);
1404 ConicGradientEffectD2D1::Unregister(mFactory);
1405 ExtendInputEffectD2D1::Unregister(mFactory);
1406 mFactory = nullptr;
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
1418 // layers pushed.
1419 PopAllClips();
1420 mUsedCommandListsSincePurge = 0;
1421 mTransformedGlyphsSinceLastPurge = 0;
1422 mDC->EndDraw();
1423 mDC->BeginDraw();
1424 } else {
1425 mDC->Flush();
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;
1449 // Don't retry.
1450 mInitState = InitState::Failure;
1452 HRESULT hr;
1454 RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1455 if (!device) {
1456 gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1457 "DrawTargetD2D1::EnsureInitialized().";
1458 return false;
1461 hr = device->CreateDeviceContext(
1462 D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
1463 getter_AddRefs(mDC));
1465 if (FAILED(hr)) {
1466 gfxCriticalError() << "[D2D1.1] 2Failed to create a DeviceContext, code: "
1467 << hexa(hr) << " format " << (int)mFormat;
1468 return false;
1471 if (!mSurface) {
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 "
1476 << mSize;
1477 return false;
1480 D2D1_BITMAP_PROPERTIES1 props;
1481 props.dpiX = 96;
1482 props.dpiY = 96;
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));
1489 if (FAILED(hr)) {
1490 gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << mSize
1491 << " Code: " << hexa(hr) << " format " << (int)mFormat;
1492 return false;
1494 } else {
1495 D2D1_BITMAP_PROPERTIES1 props;
1496 props.dpiX = 96;
1497 props.dpiY = 96;
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));
1504 if (FAILED(hr)) {
1505 gfxCriticalError()
1506 << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr)
1507 << " format " << (int)mFormat;
1508 return false;
1512 mDC->SetTarget(CurrentTarget());
1514 hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0),
1515 getter_AddRefs(mSolidColorBrush));
1517 if (FAILED(hr)) {
1518 gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
1519 return false;
1522 mDC->BeginDraw();
1524 CurrentLayer().mIsOpaque = mFormat == SurfaceFormat::B8G8R8X8;
1526 if (!mSurface) {
1527 mDC->Clear();
1530 mInitState = InitState::Success;
1532 return true;
1535 void DrawTargetD2D1::MarkChanged() {
1536 if (mSnapshot) {
1537 MutexAutoLock lock(*mSnapshotLock);
1538 if (mSnapshot->hasOneRef()) {
1539 // Just destroy it, since no-one else knows about it.
1540 mSnapshot = nullptr;
1541 } else {
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) &&
1569 aClipIsComplex;
1572 bool DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp,
1573 const Pattern& aPattern) {
1574 if (!EnsureInitialized()) {
1575 return false;
1578 MarkChanged();
1580 PushAllClips();
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));
1593 return true;
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);
1608 D2D1_RECT_F rect;
1609 bool isAligned;
1610 bool clipIsComplex = CurrentLayer().mPushedClips.size() &&
1611 !GetDeviceSpaceClipRect(rect, isAligned);
1613 if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
1614 PushClipsToDC(mDC);
1617 FlushTransformToDC();
1619 return true;
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);
1629 return;
1632 D2D1_RECT_F rect;
1633 bool isAligned;
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";
1645 return;
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) {
1659 PopAllClips();
1660 if (!IsOperatorBoundByMask(aOp)) {
1661 tmpImage = GetImageForLayerContent();
1664 mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1665 D2DCompositionMode(aOp));
1667 if (tmpImage) {
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);
1679 return;
1682 RefPtr<ID2D1Effect> blendEffect;
1683 HRESULT hr =
1684 mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
1686 if (FAILED(hr) || !blendEffect) {
1687 gfxWarning() << "Failed to create blend effect!";
1688 return;
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));
1697 if (!tmpImage) {
1698 return;
1701 blendEffect->SetInput(0, tmpImage);
1702 blendEffect->SetInput(1, source);
1703 blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
1705 if (geom) {
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);
1719 } else {
1720 mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1721 D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
1724 mComplexBlendsWithListInList++;
1725 return;
1728 if (aPattern.GetType() == PatternType::CONIC_GRADIENT) {
1729 const ConicGradientPattern* pat =
1730 static_cast<const ConicGradientPattern*>(&aPattern);
1732 if (!pat->mStops ||
1733 pat->mStops->GetBackendType() != BackendType::DIRECT2D) {
1734 // Draw nothing because of no color stops
1735 return;
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: "
1743 << hexa(hr);
1744 return;
1747 PushAllClips();
1749 conicGradientEffect->SetValue(
1750 CONIC_PROP_STOP_COLLECTION,
1751 static_cast<const GradientStopsD2D*>(pat->mStops.get())
1752 ->mStopCollection);
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));
1765 return;
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) {
1773 // Draw nothing!
1774 return;
1777 if (!pat->mStops || pat->mStops->GetBackendType() != BackendType::DIRECT2D) {
1778 // Draw nothing because of no color stops
1779 return;
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: "
1788 << hexa(hr);
1789 return;
1792 PushAllClips();
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) {
1831 D2D1_RECT_F result;
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);
1840 return result;
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()) {
1849 return false;
1852 for (auto iter = CurrentLayer().mPushedClips.begin();
1853 iter != CurrentLayer().mPushedClips.end(); iter++) {
1854 if (iter->mGeometry) {
1855 return false;
1857 aClipRect = IntersectRect(aClipRect, iter->mBounds);
1858 if (!iter->mIsPixelAligned) {
1859 aIsPixelAligned = false;
1862 return true;
1865 static const uint32_t sComplexBlendsWithListAllowedInList = 4;
1867 already_AddRefed<ID2D1Image> DrawTargetD2D1::GetImageForLayerContent(
1868 const IntRect* aBounds, bool aShouldPreserveContent) {
1869 PopAllClips();
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));
1878 if (FAILED(hr)) {
1879 gfxCriticalError(
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) {
1885 mDC->Flush();
1886 return nullptr;
1888 // For now, crash in other scenarios; this should happen because tmpBitmap
1889 // is null and CopyFromBitmap call below dereferences it.
1891 mDC->Flush();
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();
1898 } else {
1899 RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
1900 mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
1901 mDC->SetTarget(CurrentTarget());
1902 list->Close();
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);
1927 if (tmpBitmap) {
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);
1955 } else {
1956 pathRect = iter->mBounds;
1957 pathRectIsAxisAligned = iter->mIsPixelAligned;
1960 iter++;
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(
1965 mCurrentClipBounds,
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)));
1969 continue;
1972 if (!pathGeom) {
1973 if (pathRectIsAxisAligned) {
1974 mCurrentClipBounds.IntersectRect(
1975 mCurrentClipBounds,
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);
1984 } else {
1985 pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
1987 } else {
1988 pathRect = IntersectRect(pathRect, iter->mBounds);
1989 pathRectIsAxisAligned = false;
1990 continue;
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);
2004 } else {
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.
2019 if (!pathGeom) {
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() {
2029 IntRect bounds;
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);
2041 sink->Close();
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) {
2056 PushClipsToDC(mDC);
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);
2073 } else {
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) {
2085 aDC->PopLayer();
2086 } else {
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);
2120 if (!pat->mStops ||
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));
2138 if (!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);
2150 if (!pat->mStops ||
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));
2170 if (!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);
2196 if (!image) {
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),
2211 nullptr, 0,
2212 D2D1::BitmapProperties1(
2213 D2D1_BITMAP_OPTIONS_TARGET,
2214 D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
2215 D2D1_ALPHA_MODE_PREMULTIPLIED)),
2216 getter_AddRefs(tmpBitmap));
2218 if (!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);
2230 image = tmpBitmap;
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(
2246 bitmap,
2247 D2D1::BitmapBrushProperties(xRepeat, yRepeat,
2248 D2DFilter(pat->mSamplingFilter)),
2249 D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(mat)),
2250 getter_AddRefs(bitmapBrush));
2251 if (!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());
2266 } else {
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(
2277 image,
2278 D2D1::ImageBrushProperties(samplingBounds, xRepeat, yRepeat,
2279 D2DInterpolationMode(pat->mSamplingFilter)),
2280 D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(mat)),
2281 getter_AddRefs(imageBrush));
2283 if (!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();
2301 if (!surface) {
2302 return nullptr;
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);
2310 } break;
2311 default: {
2312 RefPtr<DataSourceSurface> dataSurf = surface->GetDataSurface();
2313 if (!dataSurf) {
2314 gfxWarning() << "Invalid surface type.";
2315 return nullptr;
2317 Matrix transform = aUserSpace ? mTransform : Matrix();
2318 return CreatePartialBitmapForSurface(dataSurf, transform, mSize,
2319 aExtendMode, aSourceTransform, mDC,
2320 aSourceRect);
2321 } break;
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();
2335 if (!dc) {
2336 return nullptr;
2339 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
2341 std::optional<SurfaceFormat> convertTo;
2342 switch (data->GetFormat()) {
2343 case gfx::SurfaceFormat::R8G8B8X8:
2344 convertTo = SurfaceFormat::B8G8R8X8;
2345 break;
2346 case gfx::SurfaceFormat::R8G8B8A8:
2347 convertTo = SurfaceFormat::B8G8R8X8;
2348 break;
2349 default:
2350 break;
2353 if (convertTo) {
2354 const auto size = data->GetSize();
2355 const RefPtr<DrawTarget> dt =
2356 Factory::CreateDrawTarget(BackendType::SKIA, size, *convertTo);
2357 if (!dt) {
2358 return nullptr;
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())) {
2370 return nullptr;
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));
2379 if (FAILED(hr)) {
2380 gfxCriticalError(CriticalLog::DefaultOptions(
2381 Factory::ReasonableSurfaceSize(data->GetSize())))
2382 << "[D2D1.1] 4CreateBitmap failure " << data->GetSize()
2383 << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
2387 if (!bitmap) {
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),
2413 nullptr);
2416 bool DrawTargetD2D1::IsDeviceContextValid() const {
2417 uint32_t seqNo;
2418 return mDC && Factory::GetD2D1Device(&seqNo) && seqNo == mDeviceSeq;
2421 } // namespace gfx
2422 } // namespace mozilla