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