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/. */
6 
7 #include <optional>
8 
9 #include <initguid.h>
10 #include "DrawTargetD2D1.h"
11 #include "FilterNodeSoftware.h"
12 #include "GradientStopsD2D.h"
13 #include "SourceSurfaceCapture.h"
14 #include "SourceSurfaceD2D1.h"
15 #include "SourceSurfaceDual.h"
16 #include "RadialGradientEffectD2D1.h"
17 #include "PathCapture.h"
18 
19 #include "HelpersD2D.h"
20 #include "FilterNodeD2D1.h"
21 #include "ExtendInputEffectD2D1.h"
22 #include "nsAppRunner.h"
23 #include "MainThreadUtils.h"
24 
25 #include "mozilla/Mutex.h"
26 
27 // decltype is not usable for overloaded functions.
28 typedef HRESULT(WINAPI* D2D1CreateFactoryFunc)(
29     D2D1_FACTORY_TYPE factoryType, REFIID iid,
30     CONST D2D1_FACTORY_OPTIONS* pFactoryOptions, void** factory);
31 
32 namespace mozilla {
33 namespace gfx {
34 
35 uint64_t DrawTargetD2D1::mVRAMUsageDT;
36 uint64_t DrawTargetD2D1::mVRAMUsageSS;
37 StaticRefPtr<ID2D1Factory1> DrawTargetD2D1::mFactory;
38 
39 const D2D1_MATRIX_5X4_F kLuminanceMatrix =
40     D2D1::Matrix5x4F(0, 0, 0, 0.2125f, 0, 0, 0, 0.7154f, 0, 0, 0, 0.0721f, 0, 0,
41                      0, 0, 0, 0, 0, 0);
42 
D2DFactory()43 RefPtr<ID2D1Factory1> D2DFactory() { return DrawTargetD2D1::factory(); }
44 
DrawTargetD2D1()45 DrawTargetD2D1::DrawTargetD2D1()
46     : mPushedLayers(1),
47       mSnapshotLock(std::make_shared<Mutex>("DrawTargetD2D1::mSnapshotLock")),
48       mUsedCommandListsSincePurge(0),
49       mTransformedGlyphsSinceLastPurge(0),
50       mComplexBlendsWithListInList(0),
51       mDeviceSeq(0),
52       mInitState(InitState::Uninitialized) {}
53 
~DrawTargetD2D1()54 DrawTargetD2D1::~DrawTargetD2D1() {
55   PopAllClips();
56 
57   if (mSnapshot) {
58     MutexAutoLock lock(*mSnapshotLock);
59     // We may hold the only reference. MarkIndependent will clear mSnapshot;
60     // keep the snapshot object alive so it doesn't get destroyed while
61     // MarkIndependent is running.
62     RefPtr<SourceSurfaceD2D1> deathGrip = mSnapshot;
63     // mSnapshot can be treated as independent of this DrawTarget since we know
64     // this DrawTarget won't change again.
65     deathGrip->MarkIndependent();
66     // mSnapshot will be cleared now.
67   }
68 
69   if (mDC && IsDeviceContextValid()) {
70     // The only way mDC can be null is if Init failed, but it can happen and the
71     // destructor is the only place where we need to check for it since the
72     // DrawTarget will destroyed right after Init fails.
73     mDC->EndDraw();
74   }
75 
76   {
77     // Until this point in the destructor it -must- still be valid for
78     // FlushInternal to be called on this.
79     StaticMutexAutoLock lock(Factory::mDTDependencyLock);
80     // Targets depending on us can break that dependency, since we're obviously
81     // not going to be modified in the future.
82     for (auto iter = mDependentTargets.begin(); iter != mDependentTargets.end();
83          iter++) {
84       (*iter)->mDependingOnTargets.erase(this);
85     }
86     // Our dependencies on other targets no longer matter.
87     for (TargetSet::iterator iter = mDependingOnTargets.begin();
88          iter != mDependingOnTargets.end(); iter++) {
89       (*iter)->mDependentTargets.erase(this);
90     }
91   }
92 }
93 
IsValid() const94 bool DrawTargetD2D1::IsValid() const {
95   if (mInitState != InitState::Uninitialized && !IsDeviceContextValid()) {
96     return false;
97   }
98   if (NS_IsMainThread()) {
99     // Uninitialized DTs are considered valid.
100     return mInitState != InitState::Failure;
101   } else {
102     return const_cast<DrawTargetD2D1*>(this)->EnsureInitialized();
103   }
104 }
105 
Snapshot()106 already_AddRefed<SourceSurface> DrawTargetD2D1::Snapshot() {
107   if (!EnsureInitialized()) {
108     return nullptr;
109   }
110 
111   MutexAutoLock lock(*mSnapshotLock);
112   if (mSnapshot) {
113     RefPtr<SourceSurface> snapshot(mSnapshot);
114     return snapshot.forget();
115   }
116   PopAllClips();
117 
118   Flush();
119 
120   mSnapshot = new SourceSurfaceD2D1(mBitmap, mDC, mFormat, mSize, this);
121 
122   RefPtr<SourceSurface> snapshot(mSnapshot);
123   return snapshot.forget();
124 }
125 
EnsureLuminanceEffect()126 bool DrawTargetD2D1::EnsureLuminanceEffect() {
127   if (mLuminanceEffect.get()) {
128     return true;
129   }
130 
131   HRESULT hr = mDC->CreateEffect(CLSID_D2D1ColorMatrix,
132                                  getter_AddRefs(mLuminanceEffect));
133   if (FAILED(hr)) {
134     gfxCriticalError() << "Failed to create luminance effect. Code: "
135                        << hexa(hr);
136     return false;
137   }
138 
139   mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX,
140                              kLuminanceMatrix);
141   mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_ALPHA_MODE,
142                              D2D1_COLORMATRIX_ALPHA_MODE_STRAIGHT);
143   return true;
144 }
145 
IntoLuminanceSource(LuminanceType aLuminanceType,float aOpacity)146 already_AddRefed<SourceSurface> DrawTargetD2D1::IntoLuminanceSource(
147     LuminanceType aLuminanceType, float aOpacity) {
148   if (!EnsureInitialized()) {
149     return nullptr;
150   }
151   if ((aLuminanceType != LuminanceType::LUMINANCE) ||
152       // See bug 1372577, some race condition where we get invalid
153       // results with D2D in the parent process. Fallback in that case.
154       XRE_IsParentProcess()) {
155     return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
156   }
157 
158   // Create the luminance effect
159   if (!EnsureLuminanceEffect()) {
160     return DrawTarget::IntoLuminanceSource(aLuminanceType, aOpacity);
161   }
162 
163   Flush();
164 
165   {
166     D2D1_MATRIX_5X4_F matrix = kLuminanceMatrix;
167     matrix._14 *= aOpacity;
168     matrix._24 *= aOpacity;
169     matrix._34 *= aOpacity;
170 
171     mLuminanceEffect->SetValue(D2D1_COLORMATRIX_PROP_COLOR_MATRIX, matrix);
172   }
173 
174   mLuminanceEffect->SetInput(0, mBitmap);
175 
176   RefPtr<ID2D1Image> luminanceOutput;
177   mLuminanceEffect->GetOutput(getter_AddRefs(luminanceOutput));
178 
179   return MakeAndAddRef<SourceSurfaceD2D1>(luminanceOutput, mDC,
180                                           SurfaceFormat::B8G8R8A8, mSize);
181 }
182 
183 // Command lists are kept around by device contexts until EndDraw is called,
184 // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
185 // are expensive though, especially relatively when little work is done, so
186 // we try to reduce the amount of times we execute these purges.
187 static const uint32_t kPushedLayersBeforePurge = 25;
188 // Rendering glyphs with different transforms causes the glyph cache to grow
189 // very large (see bug 1474883) so we must call EndDraw every so often.
190 static const uint32_t kTransformedGlyphsBeforePurge = 1000;
191 
Flush()192 void DrawTargetD2D1::Flush() { FlushInternal(); }
193 
DrawSurface(SourceSurface * aSurface,const Rect & aDest,const Rect & aSource,const DrawSurfaceOptions & aSurfOptions,const DrawOptions & aOptions)194 void DrawTargetD2D1::DrawSurface(SourceSurface* aSurface, const Rect& aDest,
195                                  const Rect& aSource,
196                                  const DrawSurfaceOptions& aSurfOptions,
197                                  const DrawOptions& aOptions) {
198   if (!PrepareForDrawing(aOptions.mCompositionOp,
199                          ColorPattern(DeviceColor()))) {
200     return;
201   }
202 
203   D2D1_RECT_F samplingBounds;
204 
205   if (aSurfOptions.mSamplingBounds == SamplingBounds::BOUNDED) {
206     samplingBounds = D2DRect(aSource);
207   } else {
208     samplingBounds = D2D1::RectF(0, 0, Float(aSurface->GetSize().width),
209                                  Float(aSurface->GetSize().height));
210   }
211 
212   Float xScale = aDest.Width() / aSource.Width();
213   Float yScale = aDest.Height() / aSource.Height();
214 
215   RefPtr<ID2D1ImageBrush> brush;
216 
217   // Here we scale the source pattern up to the size and position where we want
218   // it to be.
219   Matrix transform;
220   transform.PreTranslate(aDest.X() - aSource.X() * xScale,
221                          aDest.Y() - aSource.Y() * yScale);
222   transform.PreScale(xScale, yScale);
223 
224   RefPtr<ID2D1Image> image =
225       GetImageForSurface(aSurface, transform, ExtendMode::CLAMP);
226 
227   if (!image) {
228     gfxWarning() << *this << ": Unable to get D2D image for surface.";
229     return;
230   }
231 
232   RefPtr<ID2D1Bitmap> bitmap;
233   HRESULT hr = E_FAIL;
234   if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
235     // If this is called with a DataSourceSurface it might do a partial upload
236     // that our DrawBitmap call doesn't support.
237     hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
238   }
239 
240   if (SUCCEEDED(hr) && bitmap &&
241       aSurfOptions.mSamplingBounds == SamplingBounds::UNBOUNDED) {
242     mDC->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha,
243                     D2DFilter(aSurfOptions.mSamplingFilter), D2DRect(aSource));
244   } else {
245     // This has issues ignoring the alpha channel on windows 7 with images
246     // marked opaque.
247     MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::B8G8R8X8);
248 
249     // Bug 1275478 - D2D1 cannot draw A8 surface correctly.
250     MOZ_ASSERT(aSurface->GetFormat() != SurfaceFormat::A8);
251 
252     mDC->CreateImageBrush(
253         image,
254         D2D1::ImageBrushProperties(
255             samplingBounds, D2D1_EXTEND_MODE_CLAMP, D2D1_EXTEND_MODE_CLAMP,
256             D2DInterpolationMode(aSurfOptions.mSamplingFilter)),
257         D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(transform)),
258         getter_AddRefs(brush));
259     mDC->FillRectangle(D2DRect(aDest), brush);
260   }
261 
262   FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(DeviceColor()));
263 }
264 
DrawFilter(FilterNode * aNode,const Rect & aSourceRect,const Point & aDestPoint,const DrawOptions & aOptions)265 void DrawTargetD2D1::DrawFilter(FilterNode* aNode, const Rect& aSourceRect,
266                                 const Point& aDestPoint,
267                                 const DrawOptions& aOptions) {
268   if (aNode->GetBackendType() != FILTER_BACKEND_DIRECT2D1_1) {
269     gfxWarning() << *this << ": Incompatible filter passed to DrawFilter.";
270     return;
271   }
272 
273   if (!PrepareForDrawing(aOptions.mCompositionOp,
274                          ColorPattern(DeviceColor()))) {
275     return;
276   }
277 
278   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
279 
280   FilterNodeD2D1* node = static_cast<FilterNodeD2D1*>(aNode);
281   node->WillDraw(this);
282 
283   if (aOptions.mAlpha == 1.0f) {
284     mDC->DrawImage(node->OutputEffect(), D2DPoint(aDestPoint),
285                    D2DRect(aSourceRect));
286   } else {
287     RefPtr<ID2D1Image> image;
288     node->OutputEffect()->GetOutput(getter_AddRefs(image));
289 
290     Matrix mat = Matrix::Translation(aDestPoint);
291 
292     RefPtr<ID2D1ImageBrush> imageBrush;
293     mDC->CreateImageBrush(
294         image, D2D1::ImageBrushProperties(D2DRect(aSourceRect)),
295         D2D1::BrushProperties(aOptions.mAlpha, D2DMatrix(mat)),
296         getter_AddRefs(imageBrush));
297     mDC->FillRectangle(D2D1::RectF(aDestPoint.x, aDestPoint.y,
298                                    aDestPoint.x + aSourceRect.width,
299                                    aDestPoint.y + aSourceRect.height),
300                        imageBrush);
301   }
302 
303   FinalizeDrawing(aOptions.mCompositionOp, ColorPattern(DeviceColor()));
304 }
305 
DrawSurfaceWithShadow(SourceSurface * aSurface,const Point & aDest,const DeviceColor & aColor,const Point & aOffset,Float aSigma,CompositionOp aOperator)306 void DrawTargetD2D1::DrawSurfaceWithShadow(SourceSurface* aSurface,
307                                            const Point& aDest,
308                                            const DeviceColor& aColor,
309                                            const Point& aOffset, Float aSigma,
310                                            CompositionOp aOperator) {
311   if (!EnsureInitialized()) {
312     return;
313   }
314   MarkChanged();
315   mDC->SetTransform(D2D1::IdentityMatrix());
316   mTransformDirty = true;
317 
318   Matrix mat;
319   RefPtr<ID2D1Image> image =
320       GetImageForSurface(aSurface, mat, ExtendMode::CLAMP, nullptr, false);
321 
322   if (!image) {
323     gfxWarning() << "Couldn't get image for surface.";
324     return;
325   }
326 
327   if (!mat.IsIdentity()) {
328     gfxDebug() << *this
329                << ": At this point complex partial uploads are not supported "
330                   "for Shadow surfaces.";
331     return;
332   }
333 
334   // Step 1, create the shadow effect.
335   RefPtr<ID2D1Effect> shadowEffect;
336   HRESULT hr = mDC->CreateEffect(
337       mFormat == SurfaceFormat::A8 ? CLSID_D2D1GaussianBlur : CLSID_D2D1Shadow,
338       getter_AddRefs(shadowEffect));
339   if (FAILED(hr) || !shadowEffect) {
340     gfxWarning() << "Failed to create shadow effect. Code: " << hexa(hr);
341     return;
342   }
343   shadowEffect->SetInput(0, image);
344   if (mFormat == SurfaceFormat::A8) {
345     shadowEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_STANDARD_DEVIATION, aSigma);
346     shadowEffect->SetValue(D2D1_GAUSSIANBLUR_PROP_BORDER_MODE,
347                            D2D1_BORDER_MODE_HARD);
348   } else {
349     shadowEffect->SetValue(D2D1_SHADOW_PROP_BLUR_STANDARD_DEVIATION, aSigma);
350     D2D1_VECTOR_4F color = {aColor.r, aColor.g, aColor.b, aColor.a};
351     shadowEffect->SetValue(D2D1_SHADOW_PROP_COLOR, color);
352   }
353 
354   D2D1_POINT_2F shadowPoint = D2DPoint(aDest + aOffset);
355   mDC->DrawImage(shadowEffect, &shadowPoint, nullptr,
356                  D2D1_INTERPOLATION_MODE_LINEAR, D2DCompositionMode(aOperator));
357 
358   if (aSurface->GetFormat() != SurfaceFormat::A8) {
359     D2D1_POINT_2F imgPoint = D2DPoint(aDest);
360     mDC->DrawImage(image, &imgPoint, nullptr, D2D1_INTERPOLATION_MODE_LINEAR,
361                    D2DCompositionMode(aOperator));
362   }
363 }
364 
ClearRect(const Rect & aRect)365 void DrawTargetD2D1::ClearRect(const Rect& aRect) {
366   if (!EnsureInitialized()) {
367     return;
368   }
369 
370   if (aRect.IsEmpty()) {
371     // Nothing to be done.
372     return;
373   }
374 
375   MarkChanged();
376 
377   PopAllClips();
378 
379   PushClipRect(aRect);
380 
381   if (mTransformDirty || !mTransform.IsIdentity()) {
382     mDC->SetTransform(D2D1::IdentityMatrix());
383     mTransformDirty = true;
384   }
385 
386   D2D1_RECT_F clipRect;
387   bool isPixelAligned;
388   if (mTransform.IsRectilinear() &&
389       GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
390     mDC->PushAxisAlignedClip(clipRect, isPixelAligned
391                                            ? D2D1_ANTIALIAS_MODE_ALIASED
392                                            : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
393     mDC->Clear();
394     mDC->PopAxisAlignedClip();
395 
396     PopClip();
397     return;
398   }
399 
400   RefPtr<ID2D1CommandList> list;
401   mUsedCommandListsSincePurge++;
402   mDC->CreateCommandList(getter_AddRefs(list));
403   mDC->SetTarget(list);
404 
405   IntRect addClipRect;
406   RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
407 
408   RefPtr<ID2D1SolidColorBrush> brush;
409   mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
410                              getter_AddRefs(brush));
411   mDC->PushAxisAlignedClip(
412       D2D1::RectF(addClipRect.X(), addClipRect.Y(), addClipRect.XMost(),
413                   addClipRect.YMost()),
414       D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
415   mDC->FillGeometry(geom, brush);
416   mDC->PopAxisAlignedClip();
417 
418   mDC->SetTarget(CurrentTarget());
419   list->Close();
420 
421   mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
422                  D2D1_COMPOSITE_MODE_DESTINATION_OUT);
423 
424   PopClip();
425 
426   return;
427 }
428 
MaskSurface(const Pattern & aSource,SourceSurface * aMask,Point aOffset,const DrawOptions & aOptions)429 void DrawTargetD2D1::MaskSurface(const Pattern& aSource, SourceSurface* aMask,
430                                  Point aOffset, const DrawOptions& aOptions) {
431   if (!EnsureInitialized()) {
432     return;
433   }
434   MarkChanged();
435 
436   RefPtr<ID2D1Bitmap> bitmap;
437 
438   Matrix mat = Matrix::Translation(aOffset);
439   RefPtr<ID2D1Image> image =
440       GetImageForSurface(aMask, mat, ExtendMode::CLAMP, nullptr);
441 
442   MOZ_ASSERT(!mat.HasNonTranslation());
443   aOffset.x = mat._31;
444   aOffset.y = mat._32;
445 
446   if (!image) {
447     gfxWarning() << "Failed to get image for surface.";
448     return;
449   }
450 
451   if (!PrepareForDrawing(aOptions.mCompositionOp, aSource)) {
452     return;
453   }
454 
455   IntSize size =
456       IntSize::Truncate(aMask->GetSize().width, aMask->GetSize().height);
457   Rect dest =
458       Rect(aOffset.x + aMask->GetRect().x, aOffset.y + aMask->GetRect().y,
459            Float(size.width), Float(size.height));
460 
461   HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
462   if (!bitmap || FAILED(hr)) {
463     // D2D says if we have an actual ID2D1Image and not a bitmap underlying the
464     // object, we can't query for a bitmap. Instead, Push/PopLayer
465     gfxWarning() << "FillOpacityMask only works with Bitmap source surfaces. "
466                     "Falling back to push/pop layer";
467 
468     RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
469     RefPtr<ID2D1ImageBrush> maskBrush;
470     hr = mDC->CreateImageBrush(
471         image,
472         D2D1::ImageBrushProperties(D2D1::RectF(0, 0, size.width, size.height)),
473         D2D1::BrushProperties(
474             1.0f, D2D1::Matrix3x2F::Translation(aMask->GetRect().x,
475                                                 aMask->GetRect().y)),
476         getter_AddRefs(maskBrush));
477     MOZ_ASSERT(SUCCEEDED(hr));
478 
479     mDC->PushLayer(
480         D2D1::LayerParameters1(D2D1::InfiniteRect(), nullptr,
481                                D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
482                                D2D1::Matrix3x2F::Translation(
483                                    aMask->GetRect().x, aMask->GetRect().y),
484                                1.0f, maskBrush, D2D1_LAYER_OPTIONS1_NONE),
485         nullptr);
486 
487     mDC->FillRectangle(D2DRect(dest), source);
488     mDC->PopLayer();
489 
490     FinalizeDrawing(aOptions.mCompositionOp, aSource);
491     return;
492   } else {
493     // If this is a data source surface, we might have created a partial bitmap
494     // for this surface and only uploaded part of the mask. In that case,
495     // we have to fixup our sizes here.
496     size.width = bitmap->GetSize().width;
497     size.height = bitmap->GetSize().height;
498     dest.SetWidth(size.width);
499     dest.SetHeight(size.height);
500   }
501 
502   // FillOpacityMask only works if the antialias mode is MODE_ALIASED
503   mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
504 
505   Rect maskRect = Rect(aMask->GetRect().x, aMask->GetRect().y,
506                        Float(size.width), Float(size.height));
507   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
508   mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS,
509                        D2DRect(dest), D2DRect(maskRect));
510 
511   mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
512 
513   FinalizeDrawing(aOptions.mCompositionOp, aSource);
514 }
515 
CopySurface(SourceSurface * aSurface,const IntRect & aSourceRect,const IntPoint & aDestination)516 void DrawTargetD2D1::CopySurface(SourceSurface* aSurface,
517                                  const IntRect& aSourceRect,
518                                  const IntPoint& aDestination) {
519   if (!EnsureInitialized()) {
520     return;
521   }
522   MarkChanged();
523 
524   PopAllClips();
525 
526   mDC->SetTransform(D2D1::IdentityMatrix());
527   mTransformDirty = true;
528 
529   Matrix mat = Matrix::Translation(aDestination.x - aSourceRect.X(),
530                                    aDestination.y - aSourceRect.Y());
531   RefPtr<ID2D1Image> image =
532       GetImageForSurface(aSurface, mat, ExtendMode::CLAMP, nullptr, false);
533 
534   if (!image) {
535     gfxWarning() << "Couldn't get image for surface.";
536     return;
537   }
538 
539   if (mat.HasNonIntegerTranslation()) {
540     gfxDebug() << *this
541                << ": At this point scaled partial uploads are not supported "
542                   "for CopySurface.";
543     return;
544   }
545 
546   IntRect sourceRect = aSourceRect;
547   sourceRect.SetLeftEdge(sourceRect.X() + (aDestination.x - aSourceRect.X()) -
548                          mat._31);
549   sourceRect.SetTopEdge(sourceRect.Y() + (aDestination.y - aSourceRect.Y()) -
550                         mat._32);
551 
552   RefPtr<ID2D1Bitmap> bitmap;
553   HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
554 
555   if (SUCCEEDED(hr) && bitmap && mFormat == SurfaceFormat::A8) {
556     RefPtr<ID2D1SolidColorBrush> brush;
557     mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
558                                D2D1::BrushProperties(), getter_AddRefs(brush));
559     mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
560     mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
561     mDC->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
562     mDC->SetAntialiasMode(D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
563     mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
564     return;
565   }
566 
567   Rect srcRect(Float(sourceRect.X()), Float(sourceRect.Y()),
568                Float(aSourceRect.Width()), Float(aSourceRect.Height()));
569 
570   Rect dstRect(Float(aDestination.x), Float(aDestination.y),
571                Float(aSourceRect.Width()), Float(aSourceRect.Height()));
572 
573   if (SUCCEEDED(hr) && bitmap) {
574     mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
575     mDC->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
576                     D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
577                     D2DRect(srcRect));
578     mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
579     return;
580   }
581 
582   mDC->DrawImage(image,
583                  D2D1::Point2F(Float(aDestination.x), Float(aDestination.y)),
584                  D2DRect(srcRect), D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
585                  D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
586 }
587 
FillRect(const Rect & aRect,const Pattern & aPattern,const DrawOptions & aOptions)588 void DrawTargetD2D1::FillRect(const Rect& aRect, const Pattern& aPattern,
589                               const DrawOptions& aOptions) {
590   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
591     return;
592   }
593 
594   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
595 
596   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
597   mDC->FillRectangle(D2DRect(aRect), brush);
598 
599   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
600 }
601 
FillRoundedRect(const RoundedRect & aRect,const Pattern & aPattern,const DrawOptions & aOptions)602 void DrawTargetD2D1::FillRoundedRect(const RoundedRect& aRect,
603                                      const Pattern& aPattern,
604                                      const DrawOptions& aOptions) {
605   if (!aRect.corners.AreRadiiSame()) {
606     return DrawTarget::FillRoundedRect(aRect, aPattern, aOptions);
607   }
608 
609   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
610     return;
611   }
612 
613   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
614 
615   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
616   mDC->FillRoundedRectangle(D2DRoundedRect(aRect), brush);
617 
618   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
619 }
620 
StrokeRect(const Rect & aRect,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)621 void DrawTargetD2D1::StrokeRect(const Rect& aRect, const Pattern& aPattern,
622                                 const StrokeOptions& aStrokeOptions,
623                                 const DrawOptions& aOptions) {
624   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
625     return;
626   }
627 
628   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
629 
630   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
631   RefPtr<ID2D1StrokeStyle> strokeStyle =
632       CreateStrokeStyleForOptions(aStrokeOptions);
633 
634   mDC->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth,
635                      strokeStyle);
636 
637   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
638 }
639 
StrokeLine(const Point & aStart,const Point & aEnd,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)640 void DrawTargetD2D1::StrokeLine(const Point& aStart, const Point& aEnd,
641                                 const Pattern& aPattern,
642                                 const StrokeOptions& aStrokeOptions,
643                                 const DrawOptions& aOptions) {
644   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
645     return;
646   }
647 
648   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
649 
650   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
651   RefPtr<ID2D1StrokeStyle> strokeStyle =
652       CreateStrokeStyleForOptions(aStrokeOptions);
653 
654   mDC->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush,
655                 aStrokeOptions.mLineWidth, strokeStyle);
656 
657   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
658 }
659 
Stroke(const Path * aPath,const Pattern & aPattern,const StrokeOptions & aStrokeOptions,const DrawOptions & aOptions)660 void DrawTargetD2D1::Stroke(const Path* aPath, const Pattern& aPattern,
661                             const StrokeOptions& aStrokeOptions,
662                             const DrawOptions& aOptions) {
663   const Path* path = aPath;
664   if (aPath->GetBackendType() == BackendType::CAPTURE) {
665     path = static_cast<const PathCapture*>(aPath)->GetRealizedPath();
666   }
667 
668   if (path->GetBackendType() != BackendType::DIRECT2D1_1) {
669     gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
670     return;
671   }
672   const PathD2D* d2dPath = static_cast<const PathD2D*>(path);
673 
674   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
675     return;
676   }
677 
678   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
679 
680   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
681   RefPtr<ID2D1StrokeStyle> strokeStyle =
682       CreateStrokeStyleForOptions(aStrokeOptions);
683 
684   mDC->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth,
685                     strokeStyle);
686 
687   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
688 }
689 
Fill(const Path * aPath,const Pattern & aPattern,const DrawOptions & aOptions)690 void DrawTargetD2D1::Fill(const Path* aPath, const Pattern& aPattern,
691                           const DrawOptions& aOptions) {
692   const Path* path = aPath;
693   if (aPath && aPath->GetBackendType() == BackendType::CAPTURE) {
694     path = static_cast<const PathCapture*>(aPath)->GetRealizedPath();
695   }
696 
697   if (!path || path->GetBackendType() != BackendType::DIRECT2D1_1) {
698     gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
699     return;
700   }
701   const PathD2D* d2dPath = static_cast<const PathD2D*>(path);
702 
703   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
704     return;
705   }
706 
707   mDC->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
708 
709   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
710 
711   mDC->FillGeometry(d2dPath->mGeometry, brush);
712 
713   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
714 }
715 
FillGlyphs(ScaledFont * aFont,const GlyphBuffer & aBuffer,const Pattern & aPattern,const DrawOptions & aOptions)716 void DrawTargetD2D1::FillGlyphs(ScaledFont* aFont, const GlyphBuffer& aBuffer,
717                                 const Pattern& aPattern,
718                                 const DrawOptions& aOptions) {
719   if (aFont->GetType() != FontType::DWRITE) {
720     gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
721     return;
722   }
723 
724   ScaledFontDWrite* font = static_cast<ScaledFontDWrite*>(aFont);
725 
726   IDWriteRenderingParams* params = font->mParams;
727 
728   AntialiasMode aaMode = font->GetDefaultAAMode();
729 
730   if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
731     aaMode = aOptions.mAntialiasMode;
732   }
733 
734   if (!PrepareForDrawing(aOptions.mCompositionOp, aPattern)) {
735     return;
736   }
737 
738   bool forceClearType = false;
739   if (!CurrentLayer().mIsOpaque && mPermitSubpixelAA &&
740       aOptions.mCompositionOp == CompositionOp::OP_OVER &&
741       aaMode == AntialiasMode::SUBPIXEL) {
742     forceClearType = true;
743   }
744 
745   D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
746 
747   switch (aaMode) {
748     case AntialiasMode::NONE:
749       d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
750       break;
751     case AntialiasMode::GRAY:
752       d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
753       break;
754     case AntialiasMode::SUBPIXEL:
755       d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
756       break;
757     default:
758       d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
759   }
760 
761   if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
762       !CurrentLayer().mIsOpaque && !forceClearType) {
763     d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
764   }
765 
766   mDC->SetTextAntialiasMode(d2dAAMode);
767 
768   if (params != mTextRenderingParams) {
769     mDC->SetTextRenderingParams(params);
770     mTextRenderingParams = params;
771   }
772 
773   RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
774 
775   AutoDWriteGlyphRun autoRun;
776   DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
777 
778   bool needsRepushedLayers = false;
779   if (forceClearType) {
780     D2D1_RECT_F rect;
781     bool isAligned;
782     needsRepushedLayers = CurrentLayer().mPushedClips.size() &&
783                           !GetDeviceSpaceClipRect(rect, isAligned);
784 
785     // If we have a complex clip in our stack and we have a transparent
786     // background, and subpixel AA is permitted, we need to repush our layer
787     // stack limited by the glyph run bounds initializing our layers for
788     // subpixel AA.
789     if (needsRepushedLayers) {
790       mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
791                                   DWRITE_MEASURING_MODE_NATURAL, &rect);
792       rect.left = std::floor(rect.left);
793       rect.right = std::ceil(rect.right);
794       rect.top = std::floor(rect.top);
795       rect.bottom = std::ceil(rect.bottom);
796 
797       PopAllClips();
798 
799       if (!mTransform.IsRectilinear()) {
800         // We must limit the pixels we touch to the -user space- bounds of
801         // the glyphs being drawn. In order not to get transparent pixels
802         // copied up in our pushed layer stack.
803         D2D1_RECT_F userRect;
804         mDC->SetTransform(D2D1::IdentityMatrix());
805         mDC->GetGlyphRunWorldBounds(D2D1::Point2F(), &autoRun,
806                                     DWRITE_MEASURING_MODE_NATURAL, &userRect);
807 
808         RefPtr<ID2D1PathGeometry> path;
809         factory()->CreatePathGeometry(getter_AddRefs(path));
810         RefPtr<ID2D1GeometrySink> sink;
811         path->Open(getter_AddRefs(sink));
812         AddRectToSink(sink, userRect);
813         sink->Close();
814 
815         mDC->PushLayer(
816             D2D1::LayerParameters1(
817                 D2D1::InfiniteRect(), path, D2D1_ANTIALIAS_MODE_ALIASED,
818                 D2DMatrix(mTransform), 1.0f, nullptr,
819                 D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND |
820                     D2D1_LAYER_OPTIONS1_IGNORE_ALPHA),
821             nullptr);
822       }
823 
824       PushClipsToDC(mDC, true, rect);
825       mDC->SetTransform(D2DMatrix(mTransform));
826     }
827   }
828 
829   if (brush) {
830     mDC->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
831   }
832 
833   if (mTransform.HasNonTranslation()) {
834     mTransformedGlyphsSinceLastPurge += aBuffer.mNumGlyphs;
835   }
836 
837   if (needsRepushedLayers) {
838     PopClipsFromDC(mDC);
839 
840     if (!mTransform.IsRectilinear()) {
841       mDC->PopLayer();
842     }
843   }
844 
845   FinalizeDrawing(aOptions.mCompositionOp, aPattern);
846 }
847 
Mask(const Pattern & aSource,const Pattern & aMask,const DrawOptions & aOptions)848 void DrawTargetD2D1::Mask(const Pattern& aSource, const Pattern& aMask,
849                           const DrawOptions& aOptions) {
850   if (!PrepareForDrawing(aOptions.mCompositionOp, aSource)) {
851     return;
852   }
853 
854   RefPtr<ID2D1Brush> source = CreateBrushForPattern(aSource, aOptions.mAlpha);
855   RefPtr<ID2D1Brush> mask = CreateBrushForPattern(aMask, 1.0f);
856   mDC->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
857                                        D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
858                                        D2D1::IdentityMatrix(), 1.0f, mask),
859                  nullptr);
860 
861   Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
862   Matrix mat = mTransform;
863   mat.Invert();
864 
865   mDC->FillRectangle(D2DRect(mat.TransformBounds(rect)), source);
866 
867   mDC->PopLayer();
868 
869   FinalizeDrawing(aOptions.mCompositionOp, aSource);
870 }
871 
PushClipGeometry(ID2D1Geometry * aGeometry,const D2D1_MATRIX_3X2_F & aTransform,bool aPixelAligned)872 void DrawTargetD2D1::PushClipGeometry(ID2D1Geometry* aGeometry,
873                                       const D2D1_MATRIX_3X2_F& aTransform,
874                                       bool aPixelAligned) {
875   mCurrentClippedGeometry = nullptr;
876 
877   PushedClip clip;
878   clip.mGeometry = aGeometry;
879   clip.mTransform = aTransform;
880   clip.mIsPixelAligned = aPixelAligned;
881 
882   aGeometry->GetBounds(aTransform, &clip.mBounds);
883 
884   CurrentLayer().mPushedClips.push_back(clip);
885 
886   // The transform of clips is relative to the world matrix, since we use the
887   // total transform for the clips, make the world matrix identity.
888   mDC->SetTransform(D2D1::IdentityMatrix());
889   mTransformDirty = true;
890 
891   if (CurrentLayer().mClipsArePushed) {
892     PushD2DLayer(mDC, clip.mGeometry, clip.mTransform, clip.mIsPixelAligned);
893   }
894 }
895 
PushClip(const Path * aPath)896 void DrawTargetD2D1::PushClip(const Path* aPath) {
897   const Path* path = aPath;
898   if (aPath->GetBackendType() == BackendType::CAPTURE) {
899     path = static_cast<const PathCapture*>(aPath)->GetRealizedPath();
900   }
901 
902   if (path->GetBackendType() != BackendType::DIRECT2D1_1) {
903     gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
904     return;
905   }
906   if (!EnsureInitialized()) {
907     return;
908   }
909 
910   RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(path));
911 
912   PushClipGeometry(pathD2D->GetGeometry(), D2DMatrix(mTransform));
913 }
914 
PushClipRect(const Rect & aRect)915 void DrawTargetD2D1::PushClipRect(const Rect& aRect) {
916   if (!EnsureInitialized()) {
917     return;
918   }
919   if (!mTransform.IsRectilinear()) {
920     // Whoops, this isn't a rectangle in device space, Direct2D will not deal
921     // with this transform the way we want it to.
922     // See remarks:
923     // http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
924     RefPtr<ID2D1Geometry> geom = ConvertRectToGeometry(D2DRect(aRect));
925     return PushClipGeometry(geom, D2DMatrix(mTransform));
926   }
927 
928   mCurrentClippedGeometry = nullptr;
929 
930   PushedClip clip;
931   Rect rect = mTransform.TransformBounds(aRect);
932   IntRect intRect;
933   clip.mIsPixelAligned = rect.ToIntRect(&intRect);
934 
935   // Do not store the transform, just store the device space rectangle directly.
936   clip.mBounds = D2DRect(rect);
937 
938   CurrentLayer().mPushedClips.push_back(clip);
939 
940   mDC->SetTransform(D2D1::IdentityMatrix());
941   mTransformDirty = true;
942 
943   if (CurrentLayer().mClipsArePushed) {
944     mDC->PushAxisAlignedClip(
945         clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED
946                                            : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
947   }
948 }
949 
PushDeviceSpaceClipRects(const IntRect * aRects,uint32_t aCount)950 void DrawTargetD2D1::PushDeviceSpaceClipRects(const IntRect* aRects,
951                                               uint32_t aCount) {
952   if (!EnsureInitialized()) {
953     return;
954   }
955   // Build a path for the union of the rects.
956   RefPtr<ID2D1PathGeometry> path;
957   factory()->CreatePathGeometry(getter_AddRefs(path));
958   RefPtr<ID2D1GeometrySink> sink;
959   path->Open(getter_AddRefs(sink));
960   sink->SetFillMode(D2D1_FILL_MODE_WINDING);
961   for (uint32_t i = 0; i < aCount; i++) {
962     const IntRect& rect = aRects[i];
963     sink->BeginFigure(D2DPoint(rect.TopLeft()), D2D1_FIGURE_BEGIN_FILLED);
964     D2D1_POINT_2F lines[3] = {D2DPoint(rect.TopRight()),
965                               D2DPoint(rect.BottomRight()),
966                               D2DPoint(rect.BottomLeft())};
967     sink->AddLines(lines, 3);
968     sink->EndFigure(D2D1_FIGURE_END_CLOSED);
969   }
970   sink->Close();
971 
972   // The path is in device-space, so there is no transform needed,
973   // and all rects are pixel aligned.
974   PushClipGeometry(path, D2D1::IdentityMatrix(), true);
975 }
976 
PopClip()977 void DrawTargetD2D1::PopClip() {
978   if (!EnsureInitialized()) {
979     return;
980   }
981   mCurrentClippedGeometry = nullptr;
982   if (CurrentLayer().mPushedClips.empty()) {
983     gfxDevCrash(LogReason::UnbalancedClipStack)
984         << "DrawTargetD2D1::PopClip: No clip to pop.";
985     return;
986   }
987 
988   if (CurrentLayer().mClipsArePushed) {
989     if (CurrentLayer().mPushedClips.back().mGeometry) {
990       mDC->PopLayer();
991     } else {
992       mDC->PopAxisAlignedClip();
993     }
994   }
995   CurrentLayer().mPushedClips.pop_back();
996 }
997 
PushLayer(bool aOpaque,Float aOpacity,SourceSurface * aMask,const Matrix & aMaskTransform,const IntRect & aBounds,bool aCopyBackground)998 void DrawTargetD2D1::PushLayer(bool aOpaque, Float aOpacity,
999                                SourceSurface* aMask,
1000                                const Matrix& aMaskTransform,
1001                                const IntRect& aBounds, bool aCopyBackground) {
1002   if (!EnsureInitialized()) {
1003     return;
1004   }
1005   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
1006 
1007   if (aOpaque) {
1008     options |= D2D1_LAYER_OPTIONS1_IGNORE_ALPHA;
1009   }
1010   if (aCopyBackground) {
1011     options |= D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
1012   }
1013 
1014   RefPtr<ID2D1ImageBrush> mask;
1015   Matrix maskTransform = aMaskTransform;
1016   RefPtr<ID2D1PathGeometry> clip;
1017 
1018   if (aMask) {
1019     RefPtr<ID2D1Image> image =
1020         GetImageForSurface(aMask, maskTransform, ExtendMode::CLAMP);
1021     mDC->SetTransform(D2D1::IdentityMatrix());
1022     mTransformDirty = true;
1023 
1024     maskTransform =
1025         maskTransform.PreTranslate(aMask->GetRect().X(), aMask->GetRect().Y());
1026     // The mask is given in user space. Our layer will apply it in device space.
1027     maskTransform = maskTransform * mTransform;
1028 
1029     if (image) {
1030       IntSize maskSize = aMask->GetSize();
1031       HRESULT hr = mDC->CreateImageBrush(
1032           image,
1033           D2D1::ImageBrushProperties(
1034               D2D1::RectF(0, 0, maskSize.width, maskSize.height)),
1035           D2D1::BrushProperties(1.0f, D2DMatrix(maskTransform)),
1036           getter_AddRefs(mask));
1037       if (FAILED(hr)) {
1038         gfxWarning() << "[D2D1.1] Failed to create a ImageBrush, code: "
1039                      << hexa(hr);
1040       }
1041 
1042       factory()->CreatePathGeometry(getter_AddRefs(clip));
1043       RefPtr<ID2D1GeometrySink> sink;
1044       clip->Open(getter_AddRefs(sink));
1045       AddRectToSink(sink, D2D1::RectF(0, 0, aMask->GetSize().width,
1046                                       aMask->GetSize().height));
1047       sink->Close();
1048     } else {
1049       gfxCriticalError() << "Failed to get image for mask surface!";
1050     }
1051   }
1052 
1053   PushAllClips();
1054 
1055   mDC->PushLayer(D2D1::LayerParameters1(
1056                      D2D1::InfiniteRect(), clip, D2D1_ANTIALIAS_MODE_ALIASED,
1057                      D2DMatrix(maskTransform), aOpacity, mask, options),
1058                  nullptr);
1059   PushedLayer pushedLayer;
1060   pushedLayer.mClipsArePushed = false;
1061   pushedLayer.mIsOpaque = aOpaque;
1062   pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
1063   mPermitSubpixelAA = aOpaque;
1064 
1065   mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
1066   mPushedLayers.push_back(pushedLayer);
1067 
1068   mDC->SetTarget(CurrentTarget());
1069 
1070   mUsedCommandListsSincePurge++;
1071 }
1072 
PopLayer()1073 void DrawTargetD2D1::PopLayer() {
1074   // We must have at least one layer at all times.
1075   MOZ_ASSERT(mPushedLayers.size() > 1);
1076   MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
1077   if (!EnsureInitialized() || mPushedLayers.size() <= 1) {
1078     return;
1079   }
1080   RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
1081   mPermitSubpixelAA = CurrentLayer().mOldPermitSubpixelAA;
1082 
1083   mPushedLayers.pop_back();
1084   mDC->SetTarget(CurrentTarget());
1085 
1086   list->Close();
1087   mDC->SetTransform(D2D1::IdentityMatrix());
1088   mTransformDirty = true;
1089 
1090   DCCommandSink sink(mDC);
1091   list->Stream(&sink);
1092 
1093   mComplexBlendsWithListInList = 0;
1094 
1095   mDC->PopLayer();
1096 }
1097 
CreateSourceSurfaceFromData(unsigned char * aData,const IntSize & aSize,int32_t aStride,SurfaceFormat aFormat) const1098 already_AddRefed<SourceSurface> DrawTargetD2D1::CreateSourceSurfaceFromData(
1099     unsigned char* aData, const IntSize& aSize, int32_t aStride,
1100     SurfaceFormat aFormat) const {
1101   RefPtr<ID2D1Bitmap1> bitmap;
1102 
1103   RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
1104   if (!dc) {
1105     return nullptr;
1106   }
1107 
1108   HRESULT hr =
1109       dc->CreateBitmap(D2DIntSize(aSize), aData, aStride,
1110                        D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE,
1111                                                D2DPixelFormat(aFormat)),
1112                        getter_AddRefs(bitmap));
1113 
1114   if (FAILED(hr) || !bitmap) {
1115     gfxCriticalError(
1116         CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(aSize)))
1117         << "[D2D1.1] 1CreateBitmap failure " << aSize << " Code: " << hexa(hr)
1118         << " format " << (int)aFormat;
1119     return nullptr;
1120   }
1121 
1122   return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), dc.get(), aFormat,
1123                                           aSize);
1124 }
1125 
CreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat) const1126 already_AddRefed<DrawTarget> DrawTargetD2D1::CreateSimilarDrawTarget(
1127     const IntSize& aSize, SurfaceFormat aFormat) const {
1128   RefPtr<DrawTargetD2D1> dt = new DrawTargetD2D1();
1129 
1130   if (!dt->Init(aSize, aFormat)) {
1131     return nullptr;
1132   }
1133 
1134   return dt.forget();
1135 }
1136 
CanCreateSimilarDrawTarget(const IntSize & aSize,SurfaceFormat aFormat) const1137 bool DrawTargetD2D1::CanCreateSimilarDrawTarget(const IntSize& aSize,
1138                                                 SurfaceFormat aFormat) const {
1139   RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
1140   if (!dc) {
1141     return false;
1142   }
1143   return (dc->GetMaximumBitmapSize() >= UINT32(aSize.width) &&
1144           dc->GetMaximumBitmapSize() >= UINT32(aSize.height));
1145 }
1146 
CreateClippedDrawTarget(const Rect & aBounds,SurfaceFormat aFormat)1147 RefPtr<DrawTarget> DrawTargetD2D1::CreateClippedDrawTarget(
1148     const Rect& aBounds, SurfaceFormat aFormat) {
1149   RefPtr<DrawTarget> result;
1150 
1151   if (!aBounds.IsEmpty()) {
1152     PushClipRect(aBounds);
1153   }
1154 
1155   D2D1_RECT_F clipRect;
1156   bool isAligned;
1157   GetDeviceSpaceClipRect(clipRect, isAligned);
1158   IntRect rect = RoundedOut(ToRect(clipRect));
1159 
1160   RefPtr<DrawTarget> dt = CreateSimilarDrawTarget(rect.Size(), aFormat);
1161   result = gfx::Factory::CreateOffsetDrawTarget(dt, rect.TopLeft());
1162   result->SetTransform(mTransform);
1163   if (!aBounds.IsEmpty()) {
1164     PopClip();
1165   }
1166 
1167   return result;
1168 }
1169 
CreatePathBuilder(FillRule aFillRule) const1170 already_AddRefed<PathBuilder> DrawTargetD2D1::CreatePathBuilder(
1171     FillRule aFillRule) const {
1172   RefPtr<ID2D1PathGeometry> path;
1173   HRESULT hr = factory()->CreatePathGeometry(getter_AddRefs(path));
1174 
1175   if (FAILED(hr)) {
1176     gfxWarning() << *this << ": Failed to create Direct2D Path Geometry. Code: "
1177                  << hexa(hr);
1178     return nullptr;
1179   }
1180 
1181   RefPtr<ID2D1GeometrySink> sink;
1182   hr = path->Open(getter_AddRefs(sink));
1183   if (FAILED(hr)) {
1184     gfxWarning() << *this << ": Failed to access Direct2D Path Geometry. Code: "
1185                  << hexa(hr);
1186     return nullptr;
1187   }
1188 
1189   if (aFillRule == FillRule::FILL_WINDING) {
1190     sink->SetFillMode(D2D1_FILL_MODE_WINDING);
1191   }
1192 
1193   return MakeAndAddRef<PathBuilderD2D>(sink, path, aFillRule,
1194                                        BackendType::DIRECT2D1_1);
1195 }
1196 
CreateGradientStops(GradientStop * rawStops,uint32_t aNumStops,ExtendMode aExtendMode) const1197 already_AddRefed<GradientStops> DrawTargetD2D1::CreateGradientStops(
1198     GradientStop* rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const {
1199   if (aNumStops == 0) {
1200     gfxWarning() << *this
1201                  << ": Failed to create GradientStopCollection with no stops.";
1202     return nullptr;
1203   }
1204 
1205   D2D1_GRADIENT_STOP* stops = new D2D1_GRADIENT_STOP[aNumStops];
1206 
1207   for (uint32_t i = 0; i < aNumStops; i++) {
1208     stops[i].position = rawStops[i].offset;
1209     stops[i].color = D2DColor(rawStops[i].color);
1210   }
1211 
1212   RefPtr<ID2D1GradientStopCollection1> stopCollection;
1213 
1214   RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
1215 
1216   if (!dc) {
1217     return nullptr;
1218   }
1219 
1220   HRESULT hr = dc->CreateGradientStopCollection(
1221       stops, aNumStops, D2D1_COLOR_SPACE_SRGB, D2D1_COLOR_SPACE_SRGB,
1222       D2D1_BUFFER_PRECISION_8BPC_UNORM, D2DExtend(aExtendMode, Axis::BOTH),
1223       D2D1_COLOR_INTERPOLATION_MODE_PREMULTIPLIED,
1224       getter_AddRefs(stopCollection));
1225   delete[] stops;
1226 
1227   if (FAILED(hr)) {
1228     gfxWarning() << *this << ": Failed to create GradientStopCollection. Code: "
1229                  << hexa(hr);
1230     return nullptr;
1231   }
1232 
1233   RefPtr<ID3D11Device> device = Factory::GetDirect3D11Device();
1234   return MakeAndAddRef<GradientStopsD2D>(stopCollection, device);
1235 }
1236 
CreateFilter(FilterType aType)1237 already_AddRefed<FilterNode> DrawTargetD2D1::CreateFilter(FilterType aType) {
1238   if (!EnsureInitialized()) {
1239     return nullptr;
1240   }
1241   return FilterNodeD2D1::Create(mDC, aType);
1242 }
1243 
Init(ID3D11Texture2D * aTexture,SurfaceFormat aFormat)1244 bool DrawTargetD2D1::Init(ID3D11Texture2D* aTexture, SurfaceFormat aFormat) {
1245   RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1246   if (!device) {
1247     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1248                        "DrawTargetD2D1::Init(ID3D11Texture2D*, SurfaceFormat).";
1249     return false;
1250   }
1251 
1252   aTexture->QueryInterface(__uuidof(IDXGISurface),
1253                            (void**)((IDXGISurface**)getter_AddRefs(mSurface)));
1254   if (!mSurface) {
1255     gfxCriticalError() << "[D2D1.1] Failed to obtain a DXGI surface.";
1256     return false;
1257   }
1258 
1259   mFormat = aFormat;
1260 
1261   D3D11_TEXTURE2D_DESC desc;
1262   aTexture->GetDesc(&desc);
1263   mSize.width = desc.Width;
1264   mSize.height = desc.Height;
1265 
1266   return true;
1267 }
1268 
Init(const IntSize & aSize,SurfaceFormat aFormat)1269 bool DrawTargetD2D1::Init(const IntSize& aSize, SurfaceFormat aFormat) {
1270   RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1271   if (!device) {
1272     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1273                        "DrawTargetD2D1::Init(IntSize, SurfaceFormat).";
1274     return false;
1275   }
1276 
1277   if (!CanCreateSimilarDrawTarget(aSize, aFormat)) {
1278     // Size unsupported.
1279     return false;
1280   }
1281 
1282   mFormat = aFormat;
1283   mSize = aSize;
1284 
1285   return true;
1286 }
1287 
1288 /**
1289  * Private helpers.
1290  */
GetByteSize() const1291 uint32_t DrawTargetD2D1::GetByteSize() const {
1292   return mSize.width * mSize.height * BytesPerPixel(mFormat);
1293 }
1294 
factory()1295 RefPtr<ID2D1Factory1> DrawTargetD2D1::factory() {
1296   StaticMutexAutoLock lock(Factory::mDeviceLock);
1297 
1298   if (mFactory || !NS_IsMainThread()) {
1299     return mFactory;
1300   }
1301 
1302   // We don't allow initializing the factory off the main thread.
1303   MOZ_RELEASE_ASSERT(NS_IsMainThread());
1304 
1305   RefPtr<ID2D1Factory> factory;
1306   D2D1CreateFactoryFunc createD2DFactory;
1307   HMODULE d2dModule = LoadLibraryW(L"d2d1.dll");
1308   createD2DFactory =
1309       (D2D1CreateFactoryFunc)GetProcAddress(d2dModule, "D2D1CreateFactory");
1310 
1311   if (!createD2DFactory) {
1312     gfxWarning() << "Failed to locate D2D1CreateFactory function.";
1313     return nullptr;
1314   }
1315 
1316   D2D1_FACTORY_OPTIONS options;
1317 #ifdef _DEBUG
1318   options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
1319 #else
1320   options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
1321 #endif
1322   // options.debugLevel = D2D1_DEBUG_LEVEL_INFORMATION;
1323 
1324   HRESULT hr =
1325       createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, __uuidof(ID2D1Factory),
1326                        &options, getter_AddRefs(factory));
1327 
1328   if (FAILED(hr) || !factory) {
1329     gfxCriticalNote << "Failed to create a D2D1 content device: " << hexa(hr);
1330     return nullptr;
1331   }
1332 
1333   RefPtr<ID2D1Factory1> factory1;
1334   hr = factory->QueryInterface(__uuidof(ID2D1Factory1),
1335                                getter_AddRefs(factory1));
1336   if (FAILED(hr) || !factory1) {
1337     return nullptr;
1338   }
1339 
1340   mFactory = factory1;
1341 
1342   ExtendInputEffectD2D1::Register(mFactory);
1343   RadialGradientEffectD2D1::Register(mFactory);
1344 
1345   return mFactory;
1346 }
1347 
CleanupD2D()1348 void DrawTargetD2D1::CleanupD2D() {
1349   MOZ_RELEASE_ASSERT(NS_IsMainThread());
1350   Factory::mDeviceLock.AssertCurrentThreadOwns();
1351 
1352   if (mFactory) {
1353     RadialGradientEffectD2D1::Unregister(mFactory);
1354     ExtendInputEffectD2D1::Unregister(mFactory);
1355     mFactory = nullptr;
1356   }
1357 }
1358 
FlushInternal(bool aHasDependencyMutex)1359 void DrawTargetD2D1::FlushInternal(bool aHasDependencyMutex /* = false */) {
1360   if (IsDeviceContextValid()) {
1361     if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge ||
1362          mTransformedGlyphsSinceLastPurge >= kTransformedGlyphsBeforePurge) &&
1363         mPushedLayers.size() == 1) {
1364       // It's important to pop all clips as otherwise layers can forget about
1365       // their clip when doing an EndDraw. When we have layers pushed we cannot
1366       // easily pop all underlying clips to delay the purge until we have no
1367       // layers pushed.
1368       PopAllClips();
1369       mUsedCommandListsSincePurge = 0;
1370       mTransformedGlyphsSinceLastPurge = 0;
1371       mDC->EndDraw();
1372       mDC->BeginDraw();
1373     } else {
1374       mDC->Flush();
1375     }
1376   }
1377 
1378   Maybe<StaticMutexAutoLock> lock;
1379 
1380   if (!aHasDependencyMutex) {
1381     lock.emplace(Factory::mDTDependencyLock);
1382   }
1383 
1384   Factory::mDTDependencyLock.AssertCurrentThreadOwns();
1385   // We no longer depend on any target.
1386   for (TargetSet::iterator iter = mDependingOnTargets.begin();
1387        iter != mDependingOnTargets.end(); iter++) {
1388     (*iter)->mDependentTargets.erase(this);
1389   }
1390   mDependingOnTargets.clear();
1391 }
1392 
EnsureInitialized()1393 bool DrawTargetD2D1::EnsureInitialized() {
1394   if (mInitState != InitState::Uninitialized) {
1395     return mInitState == InitState::Success;
1396   }
1397 
1398   // Don't retry.
1399   mInitState = InitState::Failure;
1400 
1401   HRESULT hr;
1402 
1403   RefPtr<ID2D1Device> device = Factory::GetD2D1Device(&mDeviceSeq);
1404   if (!device) {
1405     gfxCriticalNote << "[D2D1.1] Failed to obtain a device for "
1406                        "DrawTargetD2D1::EnsureInitialized().";
1407     return false;
1408   }
1409 
1410   hr = device->CreateDeviceContext(
1411       D2D1_DEVICE_CONTEXT_OPTIONS_ENABLE_MULTITHREADED_OPTIMIZATIONS,
1412       getter_AddRefs(mDC));
1413 
1414   if (FAILED(hr)) {
1415     gfxCriticalError() << "[D2D1.1] 2Failed to create a DeviceContext, code: "
1416                        << hexa(hr) << " format " << (int)mFormat;
1417     return false;
1418   }
1419 
1420   if (!mSurface) {
1421     if (mDC->GetMaximumBitmapSize() < UINT32(mSize.width) ||
1422         mDC->GetMaximumBitmapSize() < UINT32(mSize.height)) {
1423       // This is 'ok', so don't assert
1424       gfxCriticalNote << "[D2D1.1] Attempt to use unsupported surface size "
1425                       << mSize;
1426       return false;
1427     }
1428 
1429     D2D1_BITMAP_PROPERTIES1 props;
1430     props.dpiX = 96;
1431     props.dpiY = 96;
1432     props.pixelFormat = D2DPixelFormat(mFormat);
1433     props.colorContext = nullptr;
1434     props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
1435     hr = mDC->CreateBitmap(D2DIntSize(mSize), nullptr, 0, props,
1436                            (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
1437 
1438     if (FAILED(hr)) {
1439       gfxCriticalError() << "[D2D1.1] 3CreateBitmap failure " << mSize
1440                          << " Code: " << hexa(hr) << " format " << (int)mFormat;
1441       return false;
1442     }
1443   } else {
1444     D2D1_BITMAP_PROPERTIES1 props;
1445     props.dpiX = 96;
1446     props.dpiY = 96;
1447     props.pixelFormat = D2DPixelFormat(mFormat);
1448     props.colorContext = nullptr;
1449     props.bitmapOptions = D2D1_BITMAP_OPTIONS_TARGET;
1450     hr = mDC->CreateBitmapFromDxgiSurface(
1451         mSurface, props, (ID2D1Bitmap1**)getter_AddRefs(mBitmap));
1452 
1453     if (FAILED(hr)) {
1454       gfxCriticalError()
1455           << "[D2D1.1] CreateBitmapFromDxgiSurface failure Code: " << hexa(hr)
1456           << " format " << (int)mFormat;
1457       return false;
1458     }
1459   }
1460 
1461   mDC->SetTarget(CurrentTarget());
1462 
1463   hr = mDC->CreateSolidColorBrush(D2D1::ColorF(0, 0),
1464                                   getter_AddRefs(mSolidColorBrush));
1465 
1466   if (FAILED(hr)) {
1467     gfxCriticalError() << "[D2D1.1] Failure creating solid color brush (I2).";
1468     return false;
1469   }
1470 
1471   mDC->BeginDraw();
1472 
1473   CurrentLayer().mIsOpaque = mFormat == SurfaceFormat::B8G8R8X8;
1474 
1475   if (!mSurface) {
1476     mDC->Clear();
1477   }
1478 
1479   mInitState = InitState::Success;
1480 
1481   return true;
1482 }
1483 
MarkChanged()1484 void DrawTargetD2D1::MarkChanged() {
1485   if (mSnapshot) {
1486     MutexAutoLock lock(*mSnapshotLock);
1487     if (mSnapshot->hasOneRef()) {
1488       // Just destroy it, since no-one else knows about it.
1489       mSnapshot = nullptr;
1490     } else {
1491       mSnapshot->DrawTargetWillChange();
1492       // The snapshot will no longer depend on this target.
1493       MOZ_ASSERT(!mSnapshot);
1494     }
1495   }
1496 
1497   {
1498     StaticMutexAutoLock lock(Factory::mDTDependencyLock);
1499     if (mDependentTargets.size()) {
1500       // Copy mDependentTargets since the Flush()es below will modify it.
1501       TargetSet tmpTargets = mDependentTargets;
1502       for (TargetSet::iterator iter = tmpTargets.begin();
1503            iter != tmpTargets.end(); iter++) {
1504         (*iter)->FlushInternal(true);
1505       }
1506       // The Flush() should have broken all dependencies on this target.
1507       MOZ_ASSERT(!mDependentTargets.size());
1508     }
1509   }
1510 }
1511 
ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,const Pattern & aPattern,bool aClipIsComplex)1512 bool DrawTargetD2D1::ShouldClipTemporarySurfaceDrawing(CompositionOp aOp,
1513                                                        const Pattern& aPattern,
1514                                                        bool aClipIsComplex) {
1515   bool patternSupported = IsPatternSupportedByD2D(aPattern);
1516   return patternSupported && !CurrentLayer().mIsOpaque &&
1517          D2DSupportsCompositeMode(aOp) && IsOperatorBoundByMask(aOp) &&
1518          aClipIsComplex;
1519 }
1520 
PrepareForDrawing(CompositionOp aOp,const Pattern & aPattern)1521 bool DrawTargetD2D1::PrepareForDrawing(CompositionOp aOp,
1522                                        const Pattern& aPattern) {
1523   if (!EnsureInitialized()) {
1524     return false;
1525   }
1526 
1527   MarkChanged();
1528 
1529   bool patternSupported = IsPatternSupportedByD2D(aPattern);
1530 
1531   if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
1532     // It's important to do this before FlushTransformToDC! As this will cause
1533     // the transform to become dirty.
1534     PushAllClips();
1535 
1536     FlushTransformToDC();
1537 
1538     if (aOp != CompositionOp::OP_OVER) {
1539       mDC->SetPrimitiveBlend(D2DPrimitiveBlendMode(aOp));
1540     }
1541 
1542     return true;
1543   }
1544 
1545   HRESULT result = mDC->CreateCommandList(getter_AddRefs(mCommandList));
1546   mDC->SetTarget(mCommandList);
1547   mUsedCommandListsSincePurge++;
1548 
1549   // This is where we should have a valid command list.  If we don't, something
1550   // is wrong, and it's likely an OOM.
1551   if (!mCommandList) {
1552     gfxDevCrash(LogReason::InvalidCommandList)
1553         << "Invalid D2D1.1 command list on creation "
1554         << mUsedCommandListsSincePurge << ", " << gfx::hexa(result);
1555   }
1556 
1557   D2D1_RECT_F rect;
1558   bool isAligned;
1559   bool clipIsComplex = CurrentLayer().mPushedClips.size() &&
1560                        !GetDeviceSpaceClipRect(rect, isAligned);
1561 
1562   if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
1563     PushClipsToDC(mDC);
1564   }
1565 
1566   FlushTransformToDC();
1567 
1568   return true;
1569 }
1570 
FinalizeDrawing(CompositionOp aOp,const Pattern & aPattern)1571 void DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp,
1572                                      const Pattern& aPattern) {
1573   bool patternSupported = IsPatternSupportedByD2D(aPattern);
1574 
1575   if (D2DSupportsPrimitiveBlendMode(aOp) && patternSupported) {
1576     if (aOp != CompositionOp::OP_OVER)
1577       mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
1578     return;
1579   }
1580 
1581   D2D1_RECT_F rect;
1582   bool isAligned;
1583   bool clipIsComplex = CurrentLayer().mPushedClips.size() &&
1584                        !GetDeviceSpaceClipRect(rect, isAligned);
1585 
1586   if (ShouldClipTemporarySurfaceDrawing(aOp, aPattern, clipIsComplex)) {
1587     PopClipsFromDC(mDC);
1588   }
1589 
1590   mDC->SetTarget(CurrentTarget());
1591   if (!mCommandList) {
1592     gfxDevCrash(LogReason::InvalidCommandList)
1593         << "Invalid D21.1 command list on finalize";
1594     return;
1595   }
1596   mCommandList->Close();
1597 
1598   RefPtr<ID2D1CommandList> source = mCommandList;
1599   mCommandList = nullptr;
1600 
1601   mDC->SetTransform(D2D1::IdentityMatrix());
1602   mTransformDirty = true;
1603 
1604   if (patternSupported) {
1605     if (D2DSupportsCompositeMode(aOp)) {
1606       RefPtr<ID2D1Image> tmpImage;
1607       if (clipIsComplex) {
1608         PopAllClips();
1609         if (!IsOperatorBoundByMask(aOp)) {
1610           tmpImage = GetImageForLayerContent();
1611         }
1612       }
1613       mDC->DrawImage(source, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1614                      D2DCompositionMode(aOp));
1615 
1616       if (tmpImage) {
1617         RefPtr<ID2D1ImageBrush> brush;
1618         RefPtr<ID2D1Geometry> inverseGeom = GetInverseClippedGeometry();
1619         mDC->CreateImageBrush(tmpImage,
1620                               D2D1::ImageBrushProperties(
1621                                   D2D1::RectF(0, 0, mSize.width, mSize.height)),
1622                               getter_AddRefs(brush));
1623 
1624         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_COPY);
1625         mDC->FillGeometry(inverseGeom, brush);
1626         mDC->SetPrimitiveBlend(D2D1_PRIMITIVE_BLEND_SOURCE_OVER);
1627       }
1628       return;
1629     }
1630 
1631     RefPtr<ID2D1Effect> blendEffect;
1632     HRESULT hr =
1633         mDC->CreateEffect(CLSID_D2D1Blend, getter_AddRefs(blendEffect));
1634 
1635     if (FAILED(hr) || !blendEffect) {
1636       gfxWarning() << "Failed to create blend effect!";
1637       return;
1638     }
1639 
1640     // We don't need to preserve the current content of this layer as the output
1641     // of the blend effect should completely replace it.
1642     RefPtr<ID2D1Image> tmpImage = GetImageForLayerContent(false);
1643     if (!tmpImage) {
1644       return;
1645     }
1646 
1647     blendEffect->SetInput(0, tmpImage);
1648     blendEffect->SetInput(1, source);
1649     blendEffect->SetValue(D2D1_BLEND_PROP_MODE, D2DBlendMode(aOp));
1650 
1651     mDC->DrawImage(blendEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1652                    D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
1653 
1654     mComplexBlendsWithListInList++;
1655     return;
1656   }
1657 
1658   const RadialGradientPattern* pat =
1659       static_cast<const RadialGradientPattern*>(&aPattern);
1660   if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
1661     // Draw nothing!
1662     return;
1663   }
1664 
1665   if (!pat->mStops) {
1666     // Draw nothing because of no color stops
1667     return;
1668   }
1669 
1670   RefPtr<ID2D1Effect> radialGradientEffect;
1671 
1672   HRESULT hr = mDC->CreateEffect(CLSID_RadialGradientEffect,
1673                                  getter_AddRefs(radialGradientEffect));
1674   if (FAILED(hr) || !radialGradientEffect) {
1675     gfxWarning() << "Failed to create radial gradient effect. Code: "
1676                  << hexa(hr);
1677     return;
1678   }
1679 
1680   radialGradientEffect->SetValue(
1681       RADIAL_PROP_STOP_COLLECTION,
1682       static_cast<const GradientStopsD2D*>(pat->mStops.get())->mStopCollection);
1683   radialGradientEffect->SetValue(
1684       RADIAL_PROP_CENTER_1, D2D1::Vector2F(pat->mCenter1.x, pat->mCenter1.y));
1685   radialGradientEffect->SetValue(
1686       RADIAL_PROP_CENTER_2, D2D1::Vector2F(pat->mCenter2.x, pat->mCenter2.y));
1687   radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_1, pat->mRadius1);
1688   radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
1689   radialGradientEffect->SetValue(RADIAL_PROP_RADIUS_2, pat->mRadius2);
1690   radialGradientEffect->SetValue(RADIAL_PROP_TRANSFORM,
1691                                  D2DMatrix(pat->mMatrix * mTransform));
1692   radialGradientEffect->SetInput(0, source);
1693 
1694   mDC->DrawImage(radialGradientEffect, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1695                  D2DCompositionMode(aOp));
1696 }
1697 
AddDependencyOnSource(SourceSurfaceD2D1 * aSource)1698 void DrawTargetD2D1::AddDependencyOnSource(SourceSurfaceD2D1* aSource) {
1699   Maybe<MutexAutoLock> snapshotLock;
1700   // We grab the SnapshotLock as well, this guaranteeds aSource->mDrawTarget
1701   // cannot be cleared in between the if statement and the dereference.
1702   if (aSource->mSnapshotLock) {
1703     snapshotLock.emplace(*aSource->mSnapshotLock);
1704   }
1705   {
1706     StaticMutexAutoLock lock(Factory::mDTDependencyLock);
1707     if (aSource->mDrawTarget &&
1708         !mDependingOnTargets.count(aSource->mDrawTarget)) {
1709       aSource->mDrawTarget->mDependentTargets.insert(this);
1710       mDependingOnTargets.insert(aSource->mDrawTarget);
1711     }
1712   }
1713 }
1714 
IntersectRect(const D2D1_RECT_F & aRect1,const D2D1_RECT_F & aRect2)1715 static D2D1_RECT_F IntersectRect(const D2D1_RECT_F& aRect1,
1716                                  const D2D1_RECT_F& aRect2) {
1717   D2D1_RECT_F result;
1718   result.left = std::max(aRect1.left, aRect2.left);
1719   result.top = std::max(aRect1.top, aRect2.top);
1720   result.right = std::min(aRect1.right, aRect2.right);
1721   result.bottom = std::min(aRect1.bottom, aRect2.bottom);
1722 
1723   result.right = std::max(result.right, result.left);
1724   result.bottom = std::max(result.bottom, result.top);
1725 
1726   return result;
1727 }
1728 
GetDeviceSpaceClipRect(D2D1_RECT_F & aClipRect,bool & aIsPixelAligned)1729 bool DrawTargetD2D1::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect,
1730                                             bool& aIsPixelAligned) {
1731   aIsPixelAligned = true;
1732   aClipRect = D2D1::RectF(0, 0, mSize.width, mSize.height);
1733 
1734   if (!CurrentLayer().mPushedClips.size()) {
1735     return false;
1736   }
1737 
1738   for (auto iter = CurrentLayer().mPushedClips.begin();
1739        iter != CurrentLayer().mPushedClips.end(); iter++) {
1740     if (iter->mGeometry) {
1741       return false;
1742     }
1743     aClipRect = IntersectRect(aClipRect, iter->mBounds);
1744     if (!iter->mIsPixelAligned) {
1745       aIsPixelAligned = false;
1746     }
1747   }
1748   return true;
1749 }
1750 
1751 static const uint32_t sComplexBlendsWithListAllowedInList = 4;
1752 
GetImageForLayerContent(bool aShouldPreserveContent)1753 already_AddRefed<ID2D1Image> DrawTargetD2D1::GetImageForLayerContent(
1754     bool aShouldPreserveContent) {
1755   PopAllClips();
1756 
1757   if (!CurrentLayer().mCurrentList) {
1758     RefPtr<ID2D1Bitmap> tmpBitmap;
1759     HRESULT hr = mDC->CreateBitmap(
1760         D2DIntSize(mSize), D2D1::BitmapProperties(D2DPixelFormat(mFormat)),
1761         getter_AddRefs(tmpBitmap));
1762     if (FAILED(hr)) {
1763       gfxCriticalError(
1764           CriticalLog::DefaultOptions(Factory::ReasonableSurfaceSize(mSize)))
1765           << "[D2D1.1] 6CreateBitmap failure " << mSize << " Code: " << hexa(hr)
1766           << " format " << (int)mFormat;
1767       // If it's a recreate target error, return and handle it elsewhere.
1768       if (hr == D2DERR_RECREATE_TARGET) {
1769         mDC->Flush();
1770         return nullptr;
1771       }
1772       // For now, crash in other scenarios; this should happen because tmpBitmap
1773       // is null and CopyFromBitmap call below dereferences it.
1774     }
1775     mDC->Flush();
1776 
1777     tmpBitmap->CopyFromBitmap(nullptr, mBitmap, nullptr);
1778     return tmpBitmap.forget();
1779   } else {
1780     RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
1781     mDC->CreateCommandList(getter_AddRefs(CurrentLayer().mCurrentList));
1782     mDC->SetTarget(CurrentTarget());
1783     list->Close();
1784 
1785     RefPtr<ID2D1Bitmap1> tmpBitmap;
1786     if (mComplexBlendsWithListInList >= sComplexBlendsWithListAllowedInList) {
1787       D2D1_BITMAP_PROPERTIES1 props = D2D1::BitmapProperties1(
1788           D2D1_BITMAP_OPTIONS_TARGET,
1789           D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
1790                             D2D1_ALPHA_MODE_PREMULTIPLIED));
1791       mDC->CreateBitmap(mBitmap->GetPixelSize(), nullptr, 0, &props,
1792                         getter_AddRefs(tmpBitmap));
1793       mDC->SetTransform(D2D1::IdentityMatrix());
1794       mDC->SetTarget(tmpBitmap);
1795       mDC->DrawImage(list, D2D1_INTERPOLATION_MODE_NEAREST_NEIGHBOR,
1796                      D2D1_COMPOSITE_MODE_BOUNDED_SOURCE_COPY);
1797       mDC->SetTarget(CurrentTarget());
1798       mComplexBlendsWithListInList = 0;
1799     }
1800 
1801     DCCommandSink sink(mDC);
1802 
1803     if (aShouldPreserveContent) {
1804       list->Stream(&sink);
1805       PushAllClips();
1806     }
1807 
1808     if (tmpBitmap) {
1809       return tmpBitmap.forget();
1810     }
1811 
1812     return list.forget();
1813   }
1814 }
1815 
GetClippedGeometry(IntRect * aClipBounds)1816 already_AddRefed<ID2D1Geometry> DrawTargetD2D1::GetClippedGeometry(
1817     IntRect* aClipBounds) {
1818   if (mCurrentClippedGeometry) {
1819     *aClipBounds = mCurrentClipBounds;
1820     RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
1821     return clippedGeometry.forget();
1822   }
1823 
1824   MOZ_ASSERT(CurrentLayer().mPushedClips.size());
1825 
1826   mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
1827 
1828   // if pathGeom is null then pathRect represents the path.
1829   RefPtr<ID2D1Geometry> pathGeom;
1830   D2D1_RECT_F pathRect;
1831   bool pathRectIsAxisAligned = false;
1832   auto iter = CurrentLayer().mPushedClips.begin();
1833 
1834   if (iter->mGeometry) {
1835     pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
1836   } else {
1837     pathRect = iter->mBounds;
1838     pathRectIsAxisAligned = iter->mIsPixelAligned;
1839   }
1840 
1841   iter++;
1842   for (; iter != CurrentLayer().mPushedClips.end(); iter++) {
1843     // Do nothing but add it to the current clip bounds.
1844     if (!iter->mGeometry && iter->mIsPixelAligned) {
1845       mCurrentClipBounds.IntersectRect(
1846           mCurrentClipBounds,
1847           IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
1848                   int32_t(iter->mBounds.right - iter->mBounds.left),
1849                   int32_t(iter->mBounds.bottom - iter->mBounds.top)));
1850       continue;
1851     }
1852 
1853     if (!pathGeom) {
1854       if (pathRectIsAxisAligned) {
1855         mCurrentClipBounds.IntersectRect(
1856             mCurrentClipBounds,
1857             IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
1858                     int32_t(pathRect.right - pathRect.left),
1859                     int32_t(pathRect.bottom - pathRect.top)));
1860       }
1861       if (iter->mGeometry) {
1862         // See if pathRect needs to go into the path geometry.
1863         if (!pathRectIsAxisAligned) {
1864           pathGeom = ConvertRectToGeometry(pathRect);
1865         } else {
1866           pathGeom = GetTransformedGeometry(iter->mGeometry, iter->mTransform);
1867         }
1868       } else {
1869         pathRect = IntersectRect(pathRect, iter->mBounds);
1870         pathRectIsAxisAligned = false;
1871         continue;
1872       }
1873     }
1874 
1875     RefPtr<ID2D1PathGeometry> newGeom;
1876     factory()->CreatePathGeometry(getter_AddRefs(newGeom));
1877 
1878     RefPtr<ID2D1GeometrySink> currentSink;
1879     newGeom->Open(getter_AddRefs(currentSink));
1880 
1881     if (iter->mGeometry) {
1882       pathGeom->CombineWithGeometry(iter->mGeometry,
1883                                     D2D1_COMBINE_MODE_INTERSECT,
1884                                     iter->mTransform, currentSink);
1885     } else {
1886       RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
1887       pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
1888                                     D2D1::IdentityMatrix(), currentSink);
1889     }
1890 
1891     currentSink->Close();
1892 
1893     pathGeom = newGeom.forget();
1894   }
1895 
1896   // For now we need mCurrentClippedGeometry to always be non-nullptr. This
1897   // method might seem a little strange but it is just fine, if pathGeom is
1898   // nullptr pathRect will always still contain 1 clip unaccounted for
1899   // regardless of mCurrentClipBounds.
1900   if (!pathGeom) {
1901     pathGeom = ConvertRectToGeometry(pathRect);
1902   }
1903   mCurrentClippedGeometry = pathGeom.forget();
1904   *aClipBounds = mCurrentClipBounds;
1905   RefPtr<ID2D1Geometry> clippedGeometry(mCurrentClippedGeometry);
1906   return clippedGeometry.forget();
1907 }
1908 
GetInverseClippedGeometry()1909 already_AddRefed<ID2D1Geometry> DrawTargetD2D1::GetInverseClippedGeometry() {
1910   IntRect bounds;
1911   RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&bounds);
1912   RefPtr<ID2D1RectangleGeometry> rectGeom;
1913   RefPtr<ID2D1PathGeometry> inverseGeom;
1914 
1915   factory()->CreateRectangleGeometry(
1916       D2D1::RectF(0, 0, mSize.width, mSize.height), getter_AddRefs(rectGeom));
1917   factory()->CreatePathGeometry(getter_AddRefs(inverseGeom));
1918   RefPtr<ID2D1GeometrySink> sink;
1919   inverseGeom->Open(getter_AddRefs(sink));
1920   rectGeom->CombineWithGeometry(geom, D2D1_COMBINE_MODE_EXCLUDE,
1921                                 D2D1::IdentityMatrix(), sink);
1922   sink->Close();
1923 
1924   return inverseGeom.forget();
1925 }
1926 
PopAllClips()1927 void DrawTargetD2D1::PopAllClips() {
1928   if (CurrentLayer().mClipsArePushed) {
1929     PopClipsFromDC(mDC);
1930 
1931     CurrentLayer().mClipsArePushed = false;
1932   }
1933 }
1934 
PushAllClips()1935 void DrawTargetD2D1::PushAllClips() {
1936   if (!CurrentLayer().mClipsArePushed) {
1937     PushClipsToDC(mDC);
1938 
1939     CurrentLayer().mClipsArePushed = true;
1940   }
1941 }
1942 
PushClipsToDC(ID2D1DeviceContext * aDC,bool aForceIgnoreAlpha,const D2D1_RECT_F & aMaxRect)1943 void DrawTargetD2D1::PushClipsToDC(ID2D1DeviceContext* aDC,
1944                                    bool aForceIgnoreAlpha,
1945                                    const D2D1_RECT_F& aMaxRect) {
1946   mDC->SetTransform(D2D1::IdentityMatrix());
1947   mTransformDirty = true;
1948 
1949   for (auto iter = CurrentLayer().mPushedClips.begin();
1950        iter != CurrentLayer().mPushedClips.end(); iter++) {
1951     if (iter->mGeometry) {
1952       PushD2DLayer(aDC, iter->mGeometry, iter->mTransform,
1953                    iter->mIsPixelAligned, aForceIgnoreAlpha, aMaxRect);
1954     } else {
1955       mDC->PushAxisAlignedClip(iter->mBounds,
1956                                iter->mIsPixelAligned
1957                                    ? D2D1_ANTIALIAS_MODE_ALIASED
1958                                    : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
1959     }
1960   }
1961 }
1962 
PopClipsFromDC(ID2D1DeviceContext * aDC)1963 void DrawTargetD2D1::PopClipsFromDC(ID2D1DeviceContext* aDC) {
1964   for (int i = CurrentLayer().mPushedClips.size() - 1; i >= 0; i--) {
1965     if (CurrentLayer().mPushedClips[i].mGeometry) {
1966       aDC->PopLayer();
1967     } else {
1968       aDC->PopAxisAlignedClip();
1969     }
1970   }
1971 }
1972 
CreateTransparentBlackBrush()1973 already_AddRefed<ID2D1Brush> DrawTargetD2D1::CreateTransparentBlackBrush() {
1974   return GetSolidColorBrush(D2D1::ColorF(0, 0));
1975 }
1976 
GetSolidColorBrush(const D2D_COLOR_F & aColor)1977 already_AddRefed<ID2D1SolidColorBrush> DrawTargetD2D1::GetSolidColorBrush(
1978     const D2D_COLOR_F& aColor) {
1979   RefPtr<ID2D1SolidColorBrush> brush = mSolidColorBrush;
1980   brush->SetColor(aColor);
1981   return brush.forget();
1982 }
1983 
CreateBrushForPattern(const Pattern & aPattern,Float aAlpha)1984 already_AddRefed<ID2D1Brush> DrawTargetD2D1::CreateBrushForPattern(
1985     const Pattern& aPattern, Float aAlpha) {
1986   if (!IsPatternSupportedByD2D(aPattern)) {
1987     return GetSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f));
1988   }
1989 
1990   if (aPattern.GetType() == PatternType::COLOR) {
1991     DeviceColor color = static_cast<const ColorPattern*>(&aPattern)->mColor;
1992     return GetSolidColorBrush(
1993         D2D1::ColorF(color.r, color.g, color.b, color.a * aAlpha));
1994   }
1995   if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
1996     RefPtr<ID2D1LinearGradientBrush> gradBrush;
1997     const LinearGradientPattern* pat =
1998         static_cast<const LinearGradientPattern*>(&aPattern);
1999 
2000     GradientStopsD2D* stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
2001 
2002     if (!stops) {
2003       gfxDebug() << "No stops specified for gradient pattern.";
2004       return CreateTransparentBlackBrush();
2005     }
2006 
2007     if (pat->mBegin == pat->mEnd) {
2008       return CreateTransparentBlackBrush();
2009     }
2010 
2011     mDC->CreateLinearGradientBrush(
2012         D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
2013                                             D2DPoint(pat->mEnd)),
2014         D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
2015         stops->mStopCollection, getter_AddRefs(gradBrush));
2016 
2017     if (!gradBrush) {
2018       gfxWarning() << "Couldn't create gradient brush.";
2019       return CreateTransparentBlackBrush();
2020     }
2021 
2022     return gradBrush.forget();
2023   }
2024   if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
2025     RefPtr<ID2D1RadialGradientBrush> gradBrush;
2026     const RadialGradientPattern* pat =
2027         static_cast<const RadialGradientPattern*>(&aPattern);
2028 
2029     GradientStopsD2D* stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
2030 
2031     if (!stops) {
2032       gfxDebug() << "No stops specified for gradient pattern.";
2033       return CreateTransparentBlackBrush();
2034     }
2035 
2036     if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
2037       return CreateTransparentBlackBrush();
2038     }
2039 
2040     // This will not be a complex radial gradient brush.
2041     mDC->CreateRadialGradientBrush(
2042         D2D1::RadialGradientBrushProperties(
2043             D2DPoint(pat->mCenter2), D2DPoint(pat->mCenter1 - pat->mCenter2),
2044             pat->mRadius2, pat->mRadius2),
2045         D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
2046         stops->mStopCollection, getter_AddRefs(gradBrush));
2047 
2048     if (!gradBrush) {
2049       gfxWarning() << "Couldn't create gradient brush.";
2050       return CreateTransparentBlackBrush();
2051     }
2052 
2053     return gradBrush.forget();
2054   }
2055   if (aPattern.GetType() == PatternType::SURFACE) {
2056     const SurfacePattern* pat = static_cast<const SurfacePattern*>(&aPattern);
2057 
2058     if (!pat->mSurface) {
2059       gfxDebug() << "No source surface specified for surface pattern";
2060       return CreateTransparentBlackBrush();
2061     }
2062 
2063     D2D1_RECT_F samplingBounds;
2064     Matrix mat = pat->mMatrix;
2065 
2066     MOZ_ASSERT(pat->mSurface->IsValid());
2067 
2068     RefPtr<SourceSurface> surf = pat->mSurface;
2069 
2070     if (pat->mSurface->GetType() == SurfaceType::CAPTURE) {
2071       SourceSurfaceCapture* capture =
2072           static_cast<SourceSurfaceCapture*>(pat->mSurface.get());
2073       RefPtr<SourceSurface> resolved = capture->Resolve(GetBackendType());
2074       if (resolved) {
2075         surf = resolved;
2076       }
2077     }
2078 
2079     RefPtr<ID2D1Image> image = GetImageForSurface(
2080         surf, mat, pat->mExtendMode,
2081         !pat->mSamplingRect.IsEmpty() ? &pat->mSamplingRect : nullptr);
2082 
2083     if (!image) {
2084       return CreateTransparentBlackBrush();
2085     }
2086 
2087     if (surf->GetFormat() == SurfaceFormat::A8) {
2088       // See bug 1251431, at least FillOpacityMask does not appear to allow a
2089       // source bitmapbrush with source format A8. This creates a BGRA surface
2090       // with the same alpha values that the A8 surface has.
2091       RefPtr<ID2D1Bitmap> bitmap;
2092       HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
2093       if (SUCCEEDED(hr) && bitmap) {
2094         RefPtr<ID2D1Image> oldTarget;
2095         RefPtr<ID2D1Bitmap1> tmpBitmap;
2096         mDC->CreateBitmap(D2D1::SizeU(pat->mSurface->GetSize().width,
2097                                       pat->mSurface->GetSize().height),
2098                           nullptr, 0,
2099                           D2D1::BitmapProperties1(
2100                               D2D1_BITMAP_OPTIONS_TARGET,
2101                               D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM,
2102                                                 D2D1_ALPHA_MODE_PREMULTIPLIED)),
2103                           getter_AddRefs(tmpBitmap));
2104 
2105         if (!tmpBitmap) {
2106           return CreateTransparentBlackBrush();
2107         }
2108 
2109         mDC->GetTarget(getter_AddRefs(oldTarget));
2110         mDC->SetTarget(tmpBitmap);
2111 
2112         RefPtr<ID2D1SolidColorBrush> brush;
2113         mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
2114                                    getter_AddRefs(brush));
2115         mDC->FillOpacityMask(bitmap, brush);
2116         mDC->SetTarget(oldTarget);
2117         image = tmpBitmap;
2118       }
2119     }
2120 
2121     if (pat->mSamplingRect.IsEmpty()) {
2122       RefPtr<ID2D1Bitmap> bitmap;
2123       HRESULT hr = image->QueryInterface((ID2D1Bitmap**)getter_AddRefs(bitmap));
2124       if (SUCCEEDED(hr) && bitmap) {
2125         /**
2126          * Create the brush with the proper repeat modes.
2127          */
2128         RefPtr<ID2D1BitmapBrush> bitmapBrush;
2129         D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
2130         D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
2131 
2132         mDC->CreateBitmapBrush(
2133             bitmap,
2134             D2D1::BitmapBrushProperties(xRepeat, yRepeat,
2135                                         D2DFilter(pat->mSamplingFilter)),
2136             D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
2137             getter_AddRefs(bitmapBrush));
2138         if (!bitmapBrush) {
2139           gfxWarning() << "Couldn't create bitmap brush!";
2140           return CreateTransparentBlackBrush();
2141         }
2142         return bitmapBrush.forget();
2143       }
2144     }
2145 
2146     RefPtr<ID2D1ImageBrush> imageBrush;
2147     if (pat->mSamplingRect.IsEmpty()) {
2148       samplingBounds = D2D1::RectF(0, 0, Float(pat->mSurface->GetSize().width),
2149                                    Float(pat->mSurface->GetSize().height));
2150     } else if (surf->GetType() == SurfaceType::D2D1_1_IMAGE) {
2151       samplingBounds = D2DRect(pat->mSamplingRect);
2152       mat.PreTranslate(pat->mSamplingRect.X(), pat->mSamplingRect.Y());
2153     } else {
2154       // We will do a partial upload of the sampling restricted area from
2155       // GetImageForSurface.
2156       samplingBounds = D2D1::RectF(0, 0, pat->mSamplingRect.Width(),
2157                                    pat->mSamplingRect.Height());
2158     }
2159 
2160     D2D1_EXTEND_MODE xRepeat = D2DExtend(pat->mExtendMode, Axis::X_AXIS);
2161     D2D1_EXTEND_MODE yRepeat = D2DExtend(pat->mExtendMode, Axis::Y_AXIS);
2162 
2163     mDC->CreateImageBrush(
2164         image,
2165         D2D1::ImageBrushProperties(samplingBounds, xRepeat, yRepeat,
2166                                    D2DInterpolationMode(pat->mSamplingFilter)),
2167         D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
2168         getter_AddRefs(imageBrush));
2169 
2170     if (!imageBrush) {
2171       gfxWarning() << "Couldn't create image brush!";
2172       return CreateTransparentBlackBrush();
2173     }
2174 
2175     return imageBrush.forget();
2176   }
2177 
2178   gfxWarning() << "Invalid pattern type detected.";
2179   return CreateTransparentBlackBrush();
2180 }
2181 
GetImageForSurface(SourceSurface * aSurface,Matrix & aSourceTransform,ExtendMode aExtendMode,const IntRect * aSourceRect,bool aUserSpace)2182 already_AddRefed<ID2D1Image> DrawTargetD2D1::GetImageForSurface(
2183     SourceSurface* aSurface, Matrix& aSourceTransform, ExtendMode aExtendMode,
2184     const IntRect* aSourceRect, bool aUserSpace) {
2185   RefPtr<ID2D1Image> image;
2186   RefPtr<SourceSurface> surface = aSurface->GetUnderlyingSurface();
2187 
2188   if (!surface) {
2189     return nullptr;
2190   }
2191 
2192   switch (surface->GetType()) {
2193     case SurfaceType::CAPTURE: {
2194       SourceSurfaceCapture* capture =
2195           static_cast<SourceSurfaceCapture*>(surface.get());
2196       RefPtr<SourceSurface> resolved = capture->Resolve(GetBackendType());
2197       if (!resolved) {
2198         return nullptr;
2199       }
2200       MOZ_ASSERT(resolved->GetType() != SurfaceType::CAPTURE);
2201       return GetImageForSurface(resolved, aSourceTransform, aExtendMode,
2202                                 aSourceRect, aUserSpace);
2203     } break;
2204     case SurfaceType::D2D1_1_IMAGE: {
2205       SourceSurfaceD2D1* surf = static_cast<SourceSurfaceD2D1*>(surface.get());
2206       image = surf->GetImage();
2207       AddDependencyOnSource(surf);
2208     } break;
2209     case SurfaceType::DUAL_DT: {
2210       // Sometimes we have a dual drawtarget but the underlying targets
2211       // are d2d surfaces. Let's not readback and reupload in those cases.
2212       SourceSurfaceDual* dualSurface =
2213           static_cast<SourceSurfaceDual*>(surface.get());
2214       SourceSurface* first = dualSurface->GetFirstSurface();
2215       if (first->GetType() == SurfaceType::D2D1_1_IMAGE) {
2216         MOZ_ASSERT(dualSurface->SameSurfaceTypes());
2217         SourceSurfaceD2D1* d2dSurface = static_cast<SourceSurfaceD2D1*>(first);
2218         image = d2dSurface->GetImage();
2219         AddDependencyOnSource(d2dSurface);
2220         break;
2221       }
2222       // Otherwise fall through
2223     }
2224     default: {
2225       RefPtr<DataSourceSurface> dataSurf = surface->GetDataSurface();
2226       if (!dataSurf) {
2227         gfxWarning() << "Invalid surface type.";
2228         return nullptr;
2229       }
2230       Matrix transform = aUserSpace ? mTransform : Matrix();
2231       return CreatePartialBitmapForSurface(dataSurf, transform, mSize,
2232                                            aExtendMode, aSourceTransform, mDC,
2233                                            aSourceRect);
2234     } break;
2235   }
2236 
2237   return image.forget();
2238 }
2239 
OptimizeSourceSurface(SourceSurface * aSurface) const2240 already_AddRefed<SourceSurface> DrawTargetD2D1::OptimizeSourceSurface(
2241     SourceSurface* aSurface) const {
2242   if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
2243     RefPtr<SourceSurface> surface(aSurface);
2244     return surface.forget();
2245   }
2246 
2247   RefPtr<ID2D1DeviceContext> dc = Factory::GetD2DDeviceContext();
2248   if (!dc) {
2249     return nullptr;
2250   }
2251 
2252   // Special case captures so we don't resolve them to a data surface.
2253   if (aSurface->GetType() == SurfaceType::CAPTURE) {
2254     SourceSurfaceCapture* capture =
2255         static_cast<SourceSurfaceCapture*>(aSurface);
2256     RefPtr<SourceSurface> resolved = capture->Resolve(GetBackendType());
2257     if (!resolved) {
2258       return nullptr;
2259     }
2260     MOZ_ASSERT(resolved->GetType() != SurfaceType::CAPTURE);
2261     return OptimizeSourceSurface(resolved);
2262   }
2263 
2264   RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
2265 
2266   std::optional<SurfaceFormat> convertTo;
2267   switch (data->GetFormat()) {
2268     case gfx::SurfaceFormat::R8G8B8X8:
2269       convertTo = SurfaceFormat::B8G8R8X8;
2270       break;
2271     case gfx::SurfaceFormat::R8G8B8A8:
2272       convertTo = SurfaceFormat::B8G8R8X8;
2273       break;
2274     default:
2275       break;
2276   }
2277 
2278   if (convertTo) {
2279     const auto size = data->GetSize();
2280     const RefPtr<DrawTarget> dt =
2281         Factory::CreateDrawTarget(BackendType::SKIA, size, *convertTo);
2282     if (!dt) {
2283       return nullptr;
2284     }
2285     dt->CopySurface(data, {{}, size}, {});
2286 
2287     const RefPtr<SourceSurface> snapshot = dt->Snapshot();
2288     data = snapshot->GetDataSurface();
2289   }
2290 
2291   RefPtr<ID2D1Bitmap1> bitmap;
2292   {
2293     DataSourceSurface::ScopedMap map(data, DataSourceSurface::READ);
2294     if (MOZ2D_WARN_IF(!map.IsMapped())) {
2295       return nullptr;
2296     }
2297 
2298     HRESULT hr = dc->CreateBitmap(
2299         D2DIntSize(data->GetSize()), map.GetData(), map.GetStride(),
2300         D2D1::BitmapProperties1(D2D1_BITMAP_OPTIONS_NONE,
2301                                 D2DPixelFormat(data->GetFormat())),
2302         getter_AddRefs(bitmap));
2303 
2304     if (FAILED(hr)) {
2305       gfxCriticalError(CriticalLog::DefaultOptions(
2306           Factory::ReasonableSurfaceSize(data->GetSize())))
2307           << "[D2D1.1] 4CreateBitmap failure " << data->GetSize()
2308           << " Code: " << hexa(hr) << " format " << (int)data->GetFormat();
2309     }
2310   }
2311 
2312   if (!bitmap) {
2313     return data.forget();
2314   }
2315 
2316   return MakeAndAddRef<SourceSurfaceD2D1>(bitmap.get(), dc.get(),
2317                                           data->GetFormat(), data->GetSize());
2318 }
2319 
PushD2DLayer(ID2D1DeviceContext * aDC,ID2D1Geometry * aGeometry,const D2D1_MATRIX_3X2_F & aTransform,bool aPixelAligned,bool aForceIgnoreAlpha,const D2D1_RECT_F & aMaxRect)2320 void DrawTargetD2D1::PushD2DLayer(ID2D1DeviceContext* aDC,
2321                                   ID2D1Geometry* aGeometry,
2322                                   const D2D1_MATRIX_3X2_F& aTransform,
2323                                   bool aPixelAligned, bool aForceIgnoreAlpha,
2324                                   const D2D1_RECT_F& aMaxRect) {
2325   D2D1_LAYER_OPTIONS1 options = D2D1_LAYER_OPTIONS1_NONE;
2326 
2327   if (CurrentLayer().mIsOpaque || aForceIgnoreAlpha) {
2328     options = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA |
2329               D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
2330   }
2331 
2332   D2D1_ANTIALIAS_MODE antialias = aPixelAligned
2333                                       ? D2D1_ANTIALIAS_MODE_ALIASED
2334                                       : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE;
2335 
2336   mDC->PushLayer(D2D1::LayerParameters1(aMaxRect, aGeometry, antialias,
2337                                         aTransform, 1.0, nullptr, options),
2338                  nullptr);
2339 }
2340 
IsDeviceContextValid() const2341 bool DrawTargetD2D1::IsDeviceContextValid() const {
2342   uint32_t seqNo;
2343   return mDC && Factory::GetD2D1Device(&seqNo) && seqNo == mDeviceSeq;
2344 }
2345 
2346 }  // namespace gfx
2347 }  // namespace mozilla
2348