Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include <initguid.h>
7 #include "DrawTargetD2D.h"
8 #include "SourceSurfaceD2D.h"
9 #ifdef USE_D2D1_1
10 #include "SourceSurfaceD2D1.h"
11 #endif
12 #include "SourceSurfaceD2DTarget.h"
13 #include "ShadersD2D.h"
14 #include "PathD2D.h"
15 #include "GradientStopsD2D.h"
16 #include "ScaledFontDWrite.h"
17 #include "ImageScaling.h"
18 #include "Logging.h"
19 #include "Tools.h"
20 #include <algorithm>
21 #include "mozilla/Constants.h"
22 #include "FilterNodeSoftware.h"
24 #ifdef USE_D2D1_1
25 #include "FilterNodeD2D1.h"
26 #endif
28 #include <dwrite.h>
30 // decltype is not usable for overloaded functions.
31 typedef HRESULT (WINAPI*D2D1CreateFactoryFunc)(
32 D2D1_FACTORY_TYPE factoryType,
33 REFIID iid,
34 CONST D2D1_FACTORY_OPTIONS *pFactoryOptions,
35 void **factory
36 );
38 using namespace std;
40 namespace mozilla {
41 namespace gfx {
43 struct Vertex {
44 float x;
45 float y;
46 };
48 ID2D1Factory *DrawTargetD2D::mFactory;
49 IDWriteFactory *DrawTargetD2D::mDWriteFactory;
50 uint64_t DrawTargetD2D::mVRAMUsageDT;
51 uint64_t DrawTargetD2D::mVRAMUsageSS;
53 // Helper class to restore surface contents that was clipped out but may have
54 // been altered by a drawing call.
55 class AutoSaveRestoreClippedOut
56 {
57 public:
58 AutoSaveRestoreClippedOut(DrawTargetD2D *aDT)
59 : mDT(aDT)
60 {}
62 void Save() {
63 if (!mDT->mPushedClips.size()) {
64 return;
65 }
67 mDT->Flush();
69 RefPtr<ID3D10Texture2D> tmpTexture;
70 IntSize size = mDT->mSize;
71 SurfaceFormat format = mDT->mFormat;
73 CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height,
74 1, 1);
75 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
77 HRESULT hr = mDT->mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture));
78 if (FAILED(hr)) {
79 gfxWarning() << "Failed to create temporary texture to hold surface data.";
80 }
81 mDT->mDevice->CopyResource(tmpTexture, mDT->mTexture);
83 D2D1_BITMAP_PROPERTIES props = D2D1::BitmapProperties(D2DPixelFormat(format));
85 RefPtr<IDXGISurface> surf;
87 tmpTexture->QueryInterface((IDXGISurface**)byRef(surf));
89 hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf,
90 &props, byRef(mOldSurfBitmap));
92 if (FAILED(hr)) {
93 gfxWarning() << "Failed to create shared bitmap for old surface.";
94 }
96 IntRect clipBounds;
97 mClippedArea = mDT->GetClippedGeometry(&clipBounds);
99 if (!clipBounds.IsEqualEdges(IntRect(IntPoint(0, 0), mDT->mSize))) {
100 // We still need to take into account clipBounds if it contains additional
101 // clipping information.
102 RefPtr<ID2D1RectangleGeometry> rectGeom;
103 factory()->CreateRectangleGeometry(D2D1::Rect(Float(clipBounds.x),
104 Float(clipBounds.y),
105 Float(clipBounds.XMost()),
106 Float(clipBounds.YMost())),
107 byRef(rectGeom));
109 mClippedArea = IntersectGeometry(mClippedArea, rectGeom);
110 }
111 }
113 ID2D1Factory *factory() { return mDT->factory(); }
115 ~AutoSaveRestoreClippedOut()
116 {
117 if (!mOldSurfBitmap) {
118 return;
119 }
121 ID2D1RenderTarget *rt = mDT->mRT;
123 // Write the area that was clipped out back to the surface. This all
124 // happens in device space.
125 rt->SetTransform(D2D1::IdentityMatrix());
126 mDT->mTransformDirty = true;
128 RefPtr<ID2D1RectangleGeometry> rectGeom;
129 factory()->CreateRectangleGeometry(
130 D2D1::RectF(0, 0, float(mDT->mSize.width), float(mDT->mSize.height)),
131 byRef(rectGeom));
133 RefPtr<ID2D1PathGeometry> invClippedArea;
134 factory()->CreatePathGeometry(byRef(invClippedArea));
135 RefPtr<ID2D1GeometrySink> sink;
136 invClippedArea->Open(byRef(sink));
138 rectGeom->CombineWithGeometry(mClippedArea, D2D1_COMBINE_MODE_EXCLUDE, nullptr, sink);
139 sink->Close();
141 RefPtr<ID2D1BitmapBrush> brush;
142 rt->CreateBitmapBrush(mOldSurfBitmap, D2D1::BitmapBrushProperties(), D2D1::BrushProperties(), byRef(brush));
144 rt->FillGeometry(invClippedArea, brush);
145 }
147 private:
149 DrawTargetD2D *mDT;
151 // If we have an operator unbound by the source, this will contain a bitmap
152 // with the old dest surface data.
153 RefPtr<ID2D1Bitmap> mOldSurfBitmap;
154 // This contains the area drawing is clipped to.
155 RefPtr<ID2D1Geometry> mClippedArea;
156 };
158 ID2D1Factory *D2DFactory()
159 {
160 return DrawTargetD2D::factory();
161 }
163 DrawTargetD2D::DrawTargetD2D()
164 : mCurrentCachedLayer(0)
165 , mClipsArePushed(false)
166 , mPrivateData(nullptr)
167 {
168 }
170 DrawTargetD2D::~DrawTargetD2D()
171 {
172 if (mRT) {
173 PopAllClips();
175 mRT->EndDraw();
177 mVRAMUsageDT -= GetByteSize();
178 }
179 if (mTempRT) {
180 mTempRT->EndDraw();
182 mVRAMUsageDT -= GetByteSize();
183 }
185 if (mSnapshot) {
186 // We may hold the only reference. MarkIndependent will clear mSnapshot;
187 // keep the snapshot object alive so it doesn't get destroyed while
188 // MarkIndependent is running.
189 RefPtr<SourceSurfaceD2DTarget> deathGrip = mSnapshot;
190 // mSnapshot can be treated as independent of this DrawTarget since we know
191 // this DrawTarget won't change again.
192 deathGrip->MarkIndependent();
193 // mSnapshot will be cleared now.
194 }
196 for (int i = 0; i < kLayerCacheSize; i++) {
197 if (mCachedLayers[i]) {
198 mCachedLayers[i] = nullptr;
199 mVRAMUsageDT -= GetByteSize();
200 }
201 }
203 // Targets depending on us can break that dependency, since we're obviously not going to
204 // be modified in the future.
205 for (TargetSet::iterator iter = mDependentTargets.begin();
206 iter != mDependentTargets.end(); iter++) {
207 (*iter)->mDependingOnTargets.erase(this);
208 }
209 // Our dependencies on other targets no longer matter.
210 for (TargetSet::iterator iter = mDependingOnTargets.begin();
211 iter != mDependingOnTargets.end(); iter++) {
212 (*iter)->mDependentTargets.erase(this);
213 }
214 }
216 /*
217 * DrawTarget Implementation
218 */
219 TemporaryRef<SourceSurface>
220 DrawTargetD2D::Snapshot()
221 {
222 if (!mSnapshot) {
223 mSnapshot = new SourceSurfaceD2DTarget(this, mTexture, mFormat);
224 Flush();
225 }
227 return mSnapshot;
228 }
230 void
231 DrawTargetD2D::Flush()
232 {
233 PopAllClips();
235 HRESULT hr = mRT->Flush();
237 if (FAILED(hr)) {
238 gfxWarning() << "Error reported when trying to flush D2D rendertarget. Code: " << hr;
239 }
241 // We no longer depend on any target.
242 for (TargetSet::iterator iter = mDependingOnTargets.begin();
243 iter != mDependingOnTargets.end(); iter++) {
244 (*iter)->mDependentTargets.erase(this);
245 }
246 mDependingOnTargets.clear();
247 }
249 void
250 DrawTargetD2D::AddDependencyOnSource(SourceSurfaceD2DTarget* aSource)
251 {
252 if (aSource->mDrawTarget && !mDependingOnTargets.count(aSource->mDrawTarget)) {
253 aSource->mDrawTarget->mDependentTargets.insert(this);
254 mDependingOnTargets.insert(aSource->mDrawTarget);
255 }
256 }
258 TemporaryRef<ID2D1Bitmap>
259 DrawTargetD2D::GetBitmapForSurface(SourceSurface *aSurface,
260 Rect &aSource)
261 {
262 RefPtr<ID2D1Bitmap> bitmap;
264 switch (aSurface->GetType()) {
266 case SurfaceType::D2D1_BITMAP:
267 {
268 SourceSurfaceD2D *srcSurf = static_cast<SourceSurfaceD2D*>(aSurface);
269 bitmap = srcSurf->GetBitmap();
270 }
271 break;
272 case SurfaceType::D2D1_DRAWTARGET:
273 {
274 SourceSurfaceD2DTarget *srcSurf = static_cast<SourceSurfaceD2DTarget*>(aSurface);
275 bitmap = srcSurf->GetBitmap(mRT);
276 AddDependencyOnSource(srcSurf);
277 }
278 break;
279 default:
280 {
281 RefPtr<DataSourceSurface> srcSurf = aSurface->GetDataSurface();
283 if (!srcSurf) {
284 gfxDebug() << "Not able to deal with non-data source surface.";
285 return nullptr;
286 }
288 // We need to include any pixels that are overlapped by aSource
289 Rect sourceRect(aSource);
290 sourceRect.RoundOut();
292 if (sourceRect.IsEmpty()) {
293 gfxDebug() << "Bitmap source is empty. DrawBitmap will silently fail.";
294 return nullptr;
295 }
297 if (sourceRect.width > mRT->GetMaximumBitmapSize() ||
298 sourceRect.height > mRT->GetMaximumBitmapSize()) {
299 gfxDebug() << "Bitmap source larger than texture size specified. DrawBitmap will silently fail.";
300 // Don't know how to deal with this yet.
301 return nullptr;
302 }
304 int stride = srcSurf->Stride();
306 unsigned char *data = srcSurf->GetData() +
307 (uint32_t)sourceRect.y * stride +
308 (uint32_t)sourceRect.x * BytesPerPixel(srcSurf->GetFormat());
310 D2D1_BITMAP_PROPERTIES props =
311 D2D1::BitmapProperties(D2DPixelFormat(srcSurf->GetFormat()));
312 mRT->CreateBitmap(D2D1::SizeU(UINT32(sourceRect.width), UINT32(sourceRect.height)), data, stride, props, byRef(bitmap));
314 // subtract the integer part leaving the fractional part
315 aSource.x -= (uint32_t)aSource.x;
316 aSource.y -= (uint32_t)aSource.y;
317 }
318 break;
319 }
321 return bitmap;
322 }
324 #ifdef USE_D2D1_1
325 TemporaryRef<ID2D1Image>
326 DrawTargetD2D::GetImageForSurface(SourceSurface *aSurface)
327 {
328 RefPtr<ID2D1Image> image;
330 if (aSurface->GetType() == SurfaceType::D2D1_1_IMAGE) {
331 image = static_cast<SourceSurfaceD2D1*>(aSurface)->GetImage();
332 static_cast<SourceSurfaceD2D1*>(aSurface)->EnsureIndependent();
333 } else {
334 Rect r(Point(), Size(aSurface->GetSize()));
335 image = GetBitmapForSurface(aSurface, r);
336 }
338 return image;
339 }
340 #endif
342 void
343 DrawTargetD2D::DrawSurface(SourceSurface *aSurface,
344 const Rect &aDest,
345 const Rect &aSource,
346 const DrawSurfaceOptions &aSurfOptions,
347 const DrawOptions &aOptions)
348 {
349 RefPtr<ID2D1Bitmap> bitmap;
351 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()));
353 PrepareForDrawing(rt);
355 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
357 Rect srcRect = aSource;
359 bitmap = GetBitmapForSurface(aSurface, srcRect);
360 if (!bitmap) {
361 return;
362 }
364 rt->DrawBitmap(bitmap, D2DRect(aDest), aOptions.mAlpha, D2DFilter(aSurfOptions.mFilter), D2DRect(srcRect));
366 FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), aDest);
367 }
369 void
370 DrawTargetD2D::DrawFilter(FilterNode *aNode,
371 const Rect &aSourceRect,
372 const Point &aDestPoint,
373 const DrawOptions &aOptions)
374 {
375 #ifdef USE_D2D1_1
376 RefPtr<ID2D1DeviceContext> dc;
377 HRESULT hr;
379 hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc));
381 if (SUCCEEDED(hr) && aNode->GetBackendType() == FILTER_BACKEND_DIRECT2D1_1) {
382 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()));
384 PrepareForDrawing(rt);
386 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
387 hr = rt->QueryInterface((ID2D1DeviceContext**)byRef(dc));
389 if (SUCCEEDED(hr)) {
390 dc->DrawImage(static_cast<FilterNodeD2D1*>(aNode)->OutputEffect(), D2DPoint(aDestPoint), D2DRect(aSourceRect));
392 Rect destRect = aSourceRect;
393 destRect.MoveBy(aDestPoint);
394 FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), destRect);
395 return;
396 }
397 }
398 #endif
400 if (aNode->GetBackendType() != FILTER_BACKEND_SOFTWARE) {
401 gfxWarning() << "Invalid filter backend passed to DrawTargetD2D!";
402 return;
403 }
405 FilterNodeSoftware* filter = static_cast<FilterNodeSoftware*>(aNode);
406 filter->Draw(this, aSourceRect, aDestPoint, aOptions);
407 }
409 void
410 DrawTargetD2D::MaskSurface(const Pattern &aSource,
411 SourceSurface *aMask,
412 Point aOffset,
413 const DrawOptions &aOptions)
414 {
415 RefPtr<ID2D1Bitmap> bitmap;
417 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()));
419 PrepareForDrawing(rt);
421 // FillOpacityMask only works if the antialias mode is MODE_ALIASED
422 rt->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
424 IntSize size = aMask->GetSize();
425 Rect maskRect = Rect(0.f, 0.f, size.width, size.height);
426 bitmap = GetBitmapForSurface(aMask, maskRect);
427 if (!bitmap) {
428 return;
429 }
431 Rect dest = Rect(aOffset.x, aOffset.y, size.width, size.height);
432 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
433 rt->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS, D2DRect(dest), D2DRect(maskRect));
435 FinalizeRTForOperation(aOptions.mCompositionOp, ColorPattern(Color()), dest);
436 }
438 void
439 DrawTargetD2D::DrawSurfaceWithShadow(SourceSurface *aSurface,
440 const Point &aDest,
441 const Color &aColor,
442 const Point &aOffset,
443 Float aSigma,
444 CompositionOp aOperator)
445 {
446 RefPtr<ID3D10ShaderResourceView> srView = nullptr;
447 if (aSurface->GetType() != SurfaceType::D2D1_DRAWTARGET) {
448 return;
449 }
451 SetScissorToRect(nullptr);
453 // XXX - This function is way too long, it should be split up soon to make
454 // it more graspable!
456 Flush();
458 AutoSaveRestoreClippedOut restoreClippedOut(this);
460 if (!IsOperatorBoundByMask(aOperator)) {
461 restoreClippedOut.Save();
462 }
464 srView = static_cast<SourceSurfaceD2DTarget*>(aSurface)->GetSRView();
466 EnsureViews();
468 if (!mTempRTView) {
469 // This view is only needed in this path.
470 HRESULT hr = mDevice->CreateRenderTargetView(mTempTexture, nullptr, byRef(mTempRTView));
472 if (FAILED(hr)) {
473 gfxWarning() << "Failure to create RenderTargetView. Code: " << hr;
474 return;
475 }
476 }
479 RefPtr<ID3D10RenderTargetView> destRTView = mRTView;
480 RefPtr<ID3D10Texture2D> destTexture;
481 HRESULT hr;
483 RefPtr<ID3D10Texture2D> maskTexture;
484 RefPtr<ID3D10ShaderResourceView> maskSRView;
485 IntRect clipBounds;
486 if (mPushedClips.size()) {
487 EnsureClipMaskTexture(&clipBounds);
489 mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(maskSRView));
490 }
492 IntSize srcSurfSize;
493 ID3D10RenderTargetView *rtViews;
494 D3D10_VIEWPORT viewport;
496 UINT stride = sizeof(Vertex);
497 UINT offset = 0;
498 ID3D10Buffer *buff = mPrivateData->mVB;
500 mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
501 mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
502 mDevice->IASetInputLayout(mPrivateData->mInputLayout);
504 mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
505 SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f));
506 mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
507 SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
509 // If we create a downsampled source surface we need to correct aOffset for that.
510 Point correctedOffset = aOffset + aDest;
512 // The 'practical' scaling factors.
513 Float dsFactorX = 1.0f;
514 Float dsFactorY = 1.0f;
516 if (aSigma > 1.7f) {
517 // In this case 9 samples of our original will not cover it. Generate the
518 // mip levels for the original and create a downsampled version from
519 // them. We generate a version downsampled so that a kernel for a sigma
520 // of 1.7 will produce the right results.
521 float blurWeights[9] = { 0.234671f, 0.197389f, 0.197389f, 0.117465f, 0.117465f, 0.049456f, 0.049456f, 0.014732f, 0.014732f };
522 mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights));
524 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
525 aSurface->GetSize().width,
526 aSurface->GetSize().height);
527 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
528 desc.MiscFlags = D3D10_RESOURCE_MISC_GENERATE_MIPS;
530 RefPtr<ID3D10Texture2D> mipTexture;
531 hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mipTexture));
533 if (FAILED(hr)) {
534 gfxWarning() << "Failure to create temporary texture. Size: " <<
535 aSurface->GetSize() << " Code: " << hr;
536 return;
537 }
539 IntSize dsSize = IntSize(int32_t(aSurface->GetSize().width * (1.7f / aSigma)),
540 int32_t(aSurface->GetSize().height * (1.7f / aSigma)));
542 if (dsSize.width < 1) {
543 dsSize.width = 1;
544 }
545 if (dsSize.height < 1) {
546 dsSize.height = 1;
547 }
549 dsFactorX = dsSize.width / Float(aSurface->GetSize().width);
550 dsFactorY = dsSize.height / Float(aSurface->GetSize().height);
551 correctedOffset.x *= dsFactorX;
552 correctedOffset.y *= dsFactorY;
554 desc = CD3D10_TEXTURE2D_DESC(DXGI_FORMAT_B8G8R8A8_UNORM,
555 dsSize.width,
556 dsSize.height, 1, 1);
557 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
558 RefPtr<ID3D10Texture2D> tmpDSTexture;
559 hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpDSTexture));
561 if (FAILED(hr)) {
562 gfxWarning() << "Failure to create temporary texture. Size: " << dsSize << " Code: " << hr;
563 return;
564 }
566 D3D10_BOX box;
567 box.left = box.top = box.front = 0;
568 box.back = 1;
569 box.right = aSurface->GetSize().width;
570 box.bottom = aSurface->GetSize().height;
571 mDevice->CopySubresourceRegion(mipTexture, 0, 0, 0, 0, static_cast<SourceSurfaceD2DTarget*>(aSurface)->mTexture, 0, &box);
573 mDevice->CreateShaderResourceView(mipTexture, nullptr, byRef(srView));
574 mDevice->GenerateMips(srView);
576 RefPtr<ID3D10RenderTargetView> dsRTView;
577 RefPtr<ID3D10ShaderResourceView> dsSRView;
578 mDevice->CreateRenderTargetView(tmpDSTexture, nullptr, byRef(dsRTView));
579 mDevice->CreateShaderResourceView(tmpDSTexture, nullptr, byRef(dsSRView));
581 // We're not guaranteed the texture we created will be empty, we've
582 // seen old content at least on NVidia drivers.
583 float color[4] = { 0, 0, 0, 0 };
584 mDevice->ClearRenderTargetView(dsRTView, color);
586 rtViews = dsRTView;
587 mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
589 viewport.MaxDepth = 1;
590 viewport.MinDepth = 0;
591 viewport.Height = dsSize.height;
592 viewport.Width = dsSize.width;
593 viewport.TopLeftX = 0;
594 viewport.TopLeftY = 0;
596 mDevice->RSSetViewports(1, &viewport);
597 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
598 mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
599 GetPassByIndex(0)->Apply(0);
601 mDevice->OMSetBlendState(GetBlendStateForOperator(CompositionOp::OP_OVER), nullptr, 0xffffffff);
603 mDevice->Draw(4, 0);
605 srcSurfSize = dsSize;
607 srView = dsSRView;
608 } else {
609 // In this case generate a kernel to draw the blur directly to the temp
610 // surf in one direction and to final in the other.
611 float blurWeights[9];
613 float normalizeFactor = 1.0f;
614 if (aSigma != 0) {
615 normalizeFactor = 1.0f / Float(sqrt(2 * M_PI * pow(aSigma, 2)));
616 }
618 blurWeights[0] = normalizeFactor;
620 // XXX - We should actually optimize for Sigma = 0 here. We could use a
621 // much simpler shader and save a lot of texture lookups.
622 for (int i = 1; i < 9; i += 2) {
623 if (aSigma != 0) {
624 blurWeights[i] = blurWeights[i + 1] = normalizeFactor *
625 exp(-pow(float((i + 1) / 2), 2) / (2 * pow(aSigma, 2)));
626 } else {
627 blurWeights[i] = blurWeights[i + 1] = 0;
628 }
629 }
631 mPrivateData->mEffect->GetVariableByName("BlurWeights")->SetRawValue(blurWeights, 0, sizeof(blurWeights));
633 viewport.MaxDepth = 1;
634 viewport.MinDepth = 0;
635 viewport.Height = aSurface->GetSize().height;
636 viewport.Width = aSurface->GetSize().width;
637 viewport.TopLeftX = 0;
638 viewport.TopLeftY = 0;
640 mDevice->RSSetViewports(1, &viewport);
642 srcSurfSize = aSurface->GetSize();
643 }
645 // We may need to draw to a different intermediate surface if our temp
646 // texture isn't big enough.
647 bool needBiggerTemp = srcSurfSize.width > mSize.width ||
648 srcSurfSize.height > mSize.height;
650 RefPtr<ID3D10RenderTargetView> tmpRTView;
651 RefPtr<ID3D10ShaderResourceView> tmpSRView;
652 RefPtr<ID3D10Texture2D> tmpTexture;
654 IntSize tmpSurfSize = mSize;
656 if (!needBiggerTemp) {
657 tmpRTView = mTempRTView;
658 tmpSRView = mSRView;
660 // There could still be content here!
661 float color[4] = { 0, 0, 0, 0 };
662 mDevice->ClearRenderTargetView(tmpRTView, color);
663 } else {
664 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
665 srcSurfSize.width,
666 srcSurfSize.height,
667 1, 1);
668 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
670 mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture));
671 mDevice->CreateRenderTargetView(tmpTexture, nullptr, byRef(tmpRTView));
672 mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(tmpSRView));
674 tmpSurfSize = srcSurfSize;
675 }
677 rtViews = tmpRTView;
678 mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
680 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
682 // Premultiplied!
683 float shadowColor[4] = { aColor.r * aColor.a, aColor.g * aColor.a,
684 aColor.b * aColor.a, aColor.a };
685 mPrivateData->mEffect->GetVariableByName("ShadowColor")->AsVector()->
686 SetFloatVector(shadowColor);
688 float pixelOffset = 1.0f / float(srcSurfSize.width);
689 float blurOffsetsH[9] = { 0, pixelOffset, -pixelOffset,
690 2.0f * pixelOffset, -2.0f * pixelOffset,
691 3.0f * pixelOffset, -3.0f * pixelOffset,
692 4.0f * pixelOffset, - 4.0f * pixelOffset };
694 pixelOffset = 1.0f / float(tmpSurfSize.height);
695 float blurOffsetsV[9] = { 0, pixelOffset, -pixelOffset,
696 2.0f * pixelOffset, -2.0f * pixelOffset,
697 3.0f * pixelOffset, -3.0f * pixelOffset,
698 4.0f * pixelOffset, - 4.0f * pixelOffset };
700 mPrivateData->mEffect->GetVariableByName("BlurOffsetsH")->
701 SetRawValue(blurOffsetsH, 0, sizeof(blurOffsetsH));
702 mPrivateData->mEffect->GetVariableByName("BlurOffsetsV")->
703 SetRawValue(blurOffsetsV, 0, sizeof(blurOffsetsV));
705 mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
706 GetPassByIndex(0)->Apply(0);
708 mDevice->Draw(4, 0);
710 viewport.MaxDepth = 1;
711 viewport.MinDepth = 0;
712 viewport.Height = mSize.height;
713 viewport.Width = mSize.width;
714 viewport.TopLeftX = 0;
715 viewport.TopLeftY = 0;
717 mDevice->RSSetViewports(1, &viewport);
719 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(tmpSRView);
721 rtViews = destRTView;
722 mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
724 Point shadowDest = aDest + aOffset;
726 mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
727 SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((shadowDest.x / mSize.width) * 2.0f),
728 1.0f - (shadowDest.y / mSize.height * 2.0f),
729 (Float(aSurface->GetSize().width) / mSize.width) * 2.0f,
730 (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f));
731 mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
732 SetFloatVector(ShaderConstantRectD3D10(0, 0, Float(srcSurfSize.width) / tmpSurfSize.width,
733 Float(srcSurfSize.height) / tmpSurfSize.height));
735 if (mPushedClips.size()) {
736 mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView);
737 mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
738 SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height,
739 Float(aSurface->GetSize().width) / mSize.width,
740 Float(aSurface->GetSize().height) / mSize.height));
741 mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
742 GetPassByIndex(2)->Apply(0);
743 SetScissorToRect(&clipBounds);
744 } else {
745 mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
746 GetPassByIndex(1)->Apply(0);
747 }
749 mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
751 mDevice->Draw(4, 0);
753 mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
754 SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((aDest.x / mSize.width) * 2.0f),
755 1.0f - (aDest.y / mSize.height * 2.0f),
756 (Float(aSurface->GetSize().width) / mSize.width) * 2.0f,
757 (-Float(aSurface->GetSize().height) / mSize.height) * 2.0f));
758 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(static_cast<SourceSurfaceD2DTarget*>(aSurface)->GetSRView());
759 mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
760 SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
762 if (mPushedClips.size()) {
763 mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
764 SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height,
765 Float(aSurface->GetSize().width) / mSize.width,
766 Float(aSurface->GetSize().height) / mSize.height));
767 mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")->
768 GetPassByIndex(0)->Apply(0);
769 // We've set the scissor rect here for the previous draw call.
770 } else {
771 mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
772 GetPassByIndex(0)->Apply(0);
773 }
775 mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
777 mDevice->Draw(4, 0);
778 }
780 void
781 DrawTargetD2D::ClearRect(const Rect &aRect)
782 {
783 MarkChanged();
784 PushClipRect(aRect);
786 PopAllClips();
788 AutoSaveRestoreClippedOut restoreClippedOut(this);
790 D2D1_RECT_F clipRect;
791 bool isPixelAligned;
792 bool pushedClip = false;
793 if (mTransform.IsRectilinear() &&
794 GetDeviceSpaceClipRect(clipRect, isPixelAligned)) {
795 if (mTransformDirty ||
796 !mTransform.IsIdentity()) {
797 mRT->SetTransform(D2D1::IdentityMatrix());
798 mTransformDirty = true;
799 }
801 mRT->PushAxisAlignedClip(clipRect, isPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
802 pushedClip = true;
803 } else {
804 FlushTransformToRT();
805 restoreClippedOut.Save();
806 }
808 mRT->Clear(D2D1::ColorF(0, 0.0f));
810 if (pushedClip) {
811 mRT->PopAxisAlignedClip();
812 }
814 PopClip();
815 return;
816 }
818 void
819 DrawTargetD2D::CopySurface(SourceSurface *aSurface,
820 const IntRect &aSourceRect,
821 const IntPoint &aDestination)
822 {
823 MarkChanged();
825 Rect srcRect(Float(aSourceRect.x), Float(aSourceRect.y),
826 Float(aSourceRect.width), Float(aSourceRect.height));
827 Rect dstRect(Float(aDestination.x), Float(aDestination.y),
828 Float(aSourceRect.width), Float(aSourceRect.height));
830 mRT->SetTransform(D2D1::IdentityMatrix());
831 mTransformDirty = true;
832 mRT->PushAxisAlignedClip(D2DRect(dstRect), D2D1_ANTIALIAS_MODE_ALIASED);
833 mRT->Clear(D2D1::ColorF(0, 0.0f));
834 mRT->PopAxisAlignedClip();
836 RefPtr<ID2D1Bitmap> bitmap = GetBitmapForSurface(aSurface, srcRect);
837 if (!bitmap) {
838 return;
839 }
841 if (aSurface->GetFormat() == SurfaceFormat::A8) {
842 RefPtr<ID2D1SolidColorBrush> brush;
843 mRT->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White),
844 D2D1::BrushProperties(), byRef(brush));
845 mRT->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED);
846 mRT->FillOpacityMask(bitmap, brush, D2D1_OPACITY_MASK_CONTENT_GRAPHICS);
847 } else {
848 mRT->DrawBitmap(bitmap, D2DRect(dstRect), 1.0f,
849 D2D1_BITMAP_INTERPOLATION_MODE_LINEAR,
850 D2DRect(srcRect));
851 }
852 }
854 void
855 DrawTargetD2D::FillRect(const Rect &aRect,
856 const Pattern &aPattern,
857 const DrawOptions &aOptions)
858 {
859 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
861 PrepareForDrawing(rt);
863 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
865 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
867 if (brush) {
868 rt->FillRectangle(D2DRect(aRect), brush);
869 }
871 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect);
872 }
874 void
875 DrawTargetD2D::StrokeRect(const Rect &aRect,
876 const Pattern &aPattern,
877 const StrokeOptions &aStrokeOptions,
878 const DrawOptions &aOptions)
879 {
880 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
882 PrepareForDrawing(rt);
884 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
886 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
888 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
890 if (brush && strokeStyle) {
891 rt->DrawRectangle(D2DRect(aRect), brush, aStrokeOptions.mLineWidth, strokeStyle);
892 }
894 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, aRect);
895 }
897 void
898 DrawTargetD2D::StrokeLine(const Point &aStart,
899 const Point &aEnd,
900 const Pattern &aPattern,
901 const StrokeOptions &aStrokeOptions,
902 const DrawOptions &aOptions)
903 {
904 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
906 PrepareForDrawing(rt);
908 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
910 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
912 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
914 if (brush && strokeStyle) {
915 rt->DrawLine(D2DPoint(aStart), D2DPoint(aEnd), brush, aStrokeOptions.mLineWidth, strokeStyle);
916 }
918 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height)));
919 }
921 void
922 DrawTargetD2D::Stroke(const Path *aPath,
923 const Pattern &aPattern,
924 const StrokeOptions &aStrokeOptions,
925 const DrawOptions &aOptions)
926 {
927 if (aPath->GetBackendType() != BackendType::DIRECT2D) {
928 gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
929 return;
930 }
932 const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
934 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
936 PrepareForDrawing(rt);
938 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
940 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
942 RefPtr<ID2D1StrokeStyle> strokeStyle = CreateStrokeStyleForOptions(aStrokeOptions);
944 if (brush && strokeStyle) {
945 rt->DrawGeometry(d2dPath->mGeometry, brush, aStrokeOptions.mLineWidth, strokeStyle);
946 }
948 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, Float(mSize.width), Float(mSize.height)));
949 }
951 void
952 DrawTargetD2D::Fill(const Path *aPath,
953 const Pattern &aPattern,
954 const DrawOptions &aOptions)
955 {
956 if (aPath->GetBackendType() != BackendType::DIRECT2D) {
957 gfxDebug() << *this << ": Ignoring drawing call for incompatible path.";
958 return;
959 }
961 const PathD2D *d2dPath = static_cast<const PathD2D*>(aPath);
963 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
965 PrepareForDrawing(rt);
967 rt->SetAntialiasMode(D2DAAMode(aOptions.mAntialiasMode));
969 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
971 if (brush) {
972 rt->FillGeometry(d2dPath->mGeometry, brush);
973 }
975 Rect bounds;
976 if (aOptions.mCompositionOp != CompositionOp::OP_OVER) {
977 D2D1_RECT_F d2dbounds;
978 d2dPath->mGeometry->GetBounds(D2D1::IdentityMatrix(), &d2dbounds);
979 bounds = ToRect(d2dbounds);
980 }
981 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, bounds);
982 }
984 void
985 DrawTargetD2D::FillGlyphs(ScaledFont *aFont,
986 const GlyphBuffer &aBuffer,
987 const Pattern &aPattern,
988 const DrawOptions &aOptions,
989 const GlyphRenderingOptions* aRenderOptions)
990 {
991 if (aFont->GetType() != FontType::DWRITE) {
992 gfxDebug() << *this << ": Ignoring drawing call for incompatible font.";
993 return;
994 }
996 ScaledFontDWrite *font = static_cast<ScaledFontDWrite*>(aFont);
998 IDWriteRenderingParams *params = nullptr;
999 if (aRenderOptions) {
1000 if (aRenderOptions->GetType() != FontType::DWRITE) {
1001 gfxDebug() << *this << ": Ignoring incompatible GlyphRenderingOptions.";
1002 // This should never happen.
1003 MOZ_ASSERT(false);
1004 } else {
1005 params = static_cast<const GlyphRenderingOptionsDWrite*>(aRenderOptions)->mParams;
1006 }
1007 }
1009 AntialiasMode aaMode = font->GetDefaultAAMode();
1011 if (aOptions.mAntialiasMode != AntialiasMode::DEFAULT) {
1012 aaMode = aOptions.mAntialiasMode;
1013 }
1015 if (mFormat == SurfaceFormat::B8G8R8A8 && mPermitSubpixelAA &&
1016 aOptions.mCompositionOp == CompositionOp::OP_OVER && aPattern.GetType() == PatternType::COLOR &&
1017 aaMode == AntialiasMode::SUBPIXEL) {
1018 if (FillGlyphsManual(font, aBuffer,
1019 static_cast<const ColorPattern*>(&aPattern)->mColor,
1020 params, aOptions)) {
1021 return;
1022 }
1023 }
1025 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aPattern);
1027 PrepareForDrawing(rt);
1029 D2D1_TEXT_ANTIALIAS_MODE d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1031 switch (aaMode) {
1032 case AntialiasMode::NONE:
1033 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
1034 break;
1035 case AntialiasMode::GRAY:
1036 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
1037 break;
1038 case AntialiasMode::SUBPIXEL:
1039 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE;
1040 break;
1041 default:
1042 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_DEFAULT;
1043 }
1045 if (d2dAAMode == D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE &&
1046 mFormat != SurfaceFormat::B8G8R8X8) {
1047 d2dAAMode = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
1048 }
1050 rt->SetTextAntialiasMode(d2dAAMode);
1052 if (rt != mRT || params != mTextRenderingParams) {
1053 rt->SetTextRenderingParams(params);
1054 if (rt == mRT) {
1055 mTextRenderingParams = params;
1056 }
1057 }
1059 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aPattern, aOptions.mAlpha);
1061 AutoDWriteGlyphRun autoRun;
1062 DWriteGlyphRunFromGlyphs(aBuffer, font, &autoRun);
1064 if (brush) {
1065 rt->DrawGlyphRun(D2D1::Point2F(), &autoRun, brush);
1066 }
1068 FinalizeRTForOperation(aOptions.mCompositionOp, aPattern, Rect(0, 0, (Float)mSize.width, (Float)mSize.height));
1069 }
1071 void
1072 DrawTargetD2D::Mask(const Pattern &aSource,
1073 const Pattern &aMask,
1074 const DrawOptions &aOptions)
1075 {
1076 ID2D1RenderTarget *rt = GetRTForOperation(aOptions.mCompositionOp, aSource);
1078 PrepareForDrawing(rt);
1080 RefPtr<ID2D1Brush> brush = CreateBrushForPattern(aSource, aOptions.mAlpha);
1081 RefPtr<ID2D1Brush> maskBrush = CreateBrushForPattern(aMask, 1.0f);
1083 RefPtr<ID2D1Layer> layer;
1085 layer = GetCachedLayer();
1087 rt->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), nullptr,
1088 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
1089 D2D1::IdentityMatrix(),
1090 1.0f, maskBrush),
1091 layer);
1093 Rect rect(0, 0, (Float)mSize.width, (Float)mSize.height);
1094 Matrix mat = mTransform;
1095 mat.Invert();
1097 rt->FillRectangle(D2DRect(mat.TransformBounds(rect)), brush);
1098 PopCachedLayer(rt);
1100 FinalizeRTForOperation(aOptions.mCompositionOp, aSource, Rect(0, 0, (Float)mSize.width, (Float)mSize.height));
1101 }
1103 void
1104 DrawTargetD2D::PushClip(const Path *aPath)
1105 {
1106 if (aPath->GetBackendType() != BackendType::DIRECT2D) {
1107 gfxDebug() << *this << ": Ignoring clipping call for incompatible path.";
1108 return;
1109 }
1111 mCurrentClipMaskTexture = nullptr;
1112 mCurrentClippedGeometry = nullptr;
1114 RefPtr<PathD2D> pathD2D = static_cast<PathD2D*>(const_cast<Path*>(aPath));
1116 PushedClip clip;
1117 clip.mTransform = D2DMatrix(mTransform);
1118 clip.mPath = pathD2D;
1120 pathD2D->mGeometry->GetBounds(clip.mTransform, &clip.mBounds);
1122 clip.mLayer = GetCachedLayer();
1124 mPushedClips.push_back(clip);
1126 // The transform of clips is relative to the world matrix, since we use the total
1127 // transform for the clips, make the world matrix identity.
1128 mRT->SetTransform(D2D1::IdentityMatrix());
1129 mTransformDirty = true;
1131 if (mClipsArePushed) {
1132 PushD2DLayer(mRT, pathD2D->mGeometry, clip.mLayer, clip.mTransform);
1133 }
1134 }
1136 void
1137 DrawTargetD2D::PushClipRect(const Rect &aRect)
1138 {
1139 mCurrentClipMaskTexture = nullptr;
1140 mCurrentClippedGeometry = nullptr;
1141 if (!mTransform.IsRectilinear()) {
1142 // Whoops, this isn't a rectangle in device space, Direct2D will not deal
1143 // with this transform the way we want it to.
1144 // See remarks: http://msdn.microsoft.com/en-us/library/dd316860%28VS.85%29.aspx
1146 RefPtr<PathBuilder> pathBuilder = CreatePathBuilder();
1147 pathBuilder->MoveTo(aRect.TopLeft());
1148 pathBuilder->LineTo(aRect.TopRight());
1149 pathBuilder->LineTo(aRect.BottomRight());
1150 pathBuilder->LineTo(aRect.BottomLeft());
1151 pathBuilder->Close();
1152 RefPtr<Path> path = pathBuilder->Finish();
1153 return PushClip(path);
1154 }
1156 PushedClip clip;
1157 Rect rect = mTransform.TransformBounds(aRect);
1158 IntRect intRect;
1159 clip.mIsPixelAligned = rect.ToIntRect(&intRect);
1161 // Do not store the transform, just store the device space rectangle directly.
1162 clip.mBounds = D2DRect(rect);
1164 mPushedClips.push_back(clip);
1166 mRT->SetTransform(D2D1::IdentityMatrix());
1167 mTransformDirty = true;
1169 if (mClipsArePushed) {
1170 mRT->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
1171 }
1172 }
1174 void
1175 DrawTargetD2D::PopClip()
1176 {
1177 mCurrentClipMaskTexture = nullptr;
1178 mCurrentClippedGeometry = nullptr;
1179 if (mClipsArePushed) {
1180 if (mPushedClips.back().mLayer) {
1181 PopCachedLayer(mRT);
1182 } else {
1183 mRT->PopAxisAlignedClip();
1184 }
1185 }
1186 mPushedClips.pop_back();
1187 }
1189 TemporaryRef<SourceSurface>
1190 DrawTargetD2D::CreateSourceSurfaceFromData(unsigned char *aData,
1191 const IntSize &aSize,
1192 int32_t aStride,
1193 SurfaceFormat aFormat) const
1194 {
1195 RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D();
1197 if (!newSurf->InitFromData(aData, aSize, aStride, aFormat, mRT)) {
1198 return nullptr;
1199 }
1201 return newSurf;
1202 }
1204 TemporaryRef<SourceSurface>
1205 DrawTargetD2D::OptimizeSourceSurface(SourceSurface *aSurface) const
1206 {
1207 if (aSurface->GetType() == SurfaceType::D2D1_BITMAP ||
1208 aSurface->GetType() == SurfaceType::D2D1_DRAWTARGET) {
1209 return aSurface;
1210 }
1212 RefPtr<DataSourceSurface> data = aSurface->GetDataSurface();
1214 DataSourceSurface::MappedSurface map;
1215 if (!data->Map(DataSourceSurface::MapType::READ, &map)) {
1216 return nullptr;
1217 }
1219 RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D();
1220 bool success = newSurf->InitFromData(map.mData, data->GetSize(), map.mStride, data->GetFormat(), mRT);
1222 data->Unmap();
1224 if (!success) {
1225 return data;
1226 }
1227 return newSurf;
1228 }
1230 TemporaryRef<SourceSurface>
1231 DrawTargetD2D::CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
1232 {
1233 if (aSurface.mType != NativeSurfaceType::D3D10_TEXTURE) {
1234 gfxDebug() << *this << ": Failure to create source surface from non-D3D10 texture native surface.";
1235 return nullptr;
1236 }
1237 RefPtr<SourceSurfaceD2D> newSurf = new SourceSurfaceD2D();
1239 if (!newSurf->InitFromTexture(static_cast<ID3D10Texture2D*>(aSurface.mSurface),
1240 aSurface.mFormat,
1241 mRT))
1242 {
1243 gfxWarning() << *this << ": Failed to create SourceSurface from texture.";
1244 return nullptr;
1245 }
1247 return newSurf;
1248 }
1250 TemporaryRef<DrawTarget>
1251 DrawTargetD2D::CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
1252 {
1253 RefPtr<DrawTargetD2D> newTarget =
1254 new DrawTargetD2D();
1256 if (!newTarget->Init(aSize, aFormat)) {
1257 gfxDebug() << *this << ": Failed to create optimal draw target. Size: " << aSize;
1258 return nullptr;
1259 }
1261 return newTarget;
1262 }
1264 TemporaryRef<PathBuilder>
1265 DrawTargetD2D::CreatePathBuilder(FillRule aFillRule) const
1266 {
1267 RefPtr<ID2D1PathGeometry> path;
1268 HRESULT hr = factory()->CreatePathGeometry(byRef(path));
1270 if (FAILED(hr)) {
1271 gfxWarning() << "Failed to create Direct2D Path Geometry. Code: " << hr;
1272 return nullptr;
1273 }
1275 RefPtr<ID2D1GeometrySink> sink;
1276 hr = path->Open(byRef(sink));
1277 if (FAILED(hr)) {
1278 gfxWarning() << "Failed to access Direct2D Path Geometry. Code: " << hr;
1279 return nullptr;
1280 }
1282 if (aFillRule == FillRule::FILL_WINDING) {
1283 sink->SetFillMode(D2D1_FILL_MODE_WINDING);
1284 }
1286 return new PathBuilderD2D(sink, path, aFillRule);
1287 }
1289 TemporaryRef<GradientStops>
1290 DrawTargetD2D::CreateGradientStops(GradientStop *rawStops, uint32_t aNumStops, ExtendMode aExtendMode) const
1291 {
1292 D2D1_GRADIENT_STOP *stops = new D2D1_GRADIENT_STOP[aNumStops];
1294 for (uint32_t i = 0; i < aNumStops; i++) {
1295 stops[i].position = rawStops[i].offset;
1296 stops[i].color = D2DColor(rawStops[i].color);
1297 }
1299 RefPtr<ID2D1GradientStopCollection> stopCollection;
1301 HRESULT hr =
1302 mRT->CreateGradientStopCollection(stops, aNumStops,
1303 D2D1_GAMMA_2_2, D2DExtend(aExtendMode),
1304 byRef(stopCollection));
1305 delete [] stops;
1307 if (FAILED(hr)) {
1308 gfxWarning() << "Failed to create GradientStopCollection. Code: " << hr;
1309 return nullptr;
1310 }
1312 return new GradientStopsD2D(stopCollection);
1313 }
1315 TemporaryRef<FilterNode>
1316 DrawTargetD2D::CreateFilter(FilterType aType)
1317 {
1318 #ifdef USE_D2D1_1
1319 RefPtr<ID2D1DeviceContext> dc;
1320 HRESULT hr = mRT->QueryInterface((ID2D1DeviceContext**)byRef(dc));
1322 if (SUCCEEDED(hr)) {
1323 return FilterNodeD2D1::Create(this, dc, aType);
1324 }
1325 #endif
1326 return FilterNodeSoftware::Create(aType);
1327 }
1329 void*
1330 DrawTargetD2D::GetNativeSurface(NativeSurfaceType aType)
1331 {
1332 if (aType != NativeSurfaceType::D3D10_TEXTURE) {
1333 return nullptr;
1334 }
1336 return mTexture;
1337 }
1339 /*
1340 * Public functions
1341 */
1342 bool
1343 DrawTargetD2D::Init(const IntSize &aSize, SurfaceFormat aFormat)
1344 {
1345 HRESULT hr;
1347 mSize = aSize;
1348 mFormat = aFormat;
1350 if (!Factory::GetDirect3D10Device()) {
1351 gfxDebug() << "Failed to Init Direct2D DrawTarget (No D3D10 Device set.)";
1352 return false;
1353 }
1354 mDevice = Factory::GetDirect3D10Device();
1356 CD3D10_TEXTURE2D_DESC desc(DXGIFormat(aFormat),
1357 mSize.width,
1358 mSize.height,
1359 1, 1);
1360 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
1362 hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTexture));
1364 if (FAILED(hr)) {
1365 gfxDebug() << "Failed to init Direct2D DrawTarget. Size: " << mSize << " Code: " << hr;
1366 return false;
1367 }
1369 if (!InitD2DRenderTarget()) {
1370 return false;
1371 }
1373 mRT->Clear(D2D1::ColorF(0, 0));
1374 return true;
1375 }
1377 bool
1378 DrawTargetD2D::Init(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
1379 {
1380 HRESULT hr;
1382 mTexture = aTexture;
1383 mFormat = aFormat;
1385 if (!mTexture) {
1386 gfxDebug() << "No valid texture for Direct2D draw target initialization.";
1387 return false;
1388 }
1390 RefPtr<ID3D10Device> device;
1391 mTexture->GetDevice(byRef(device));
1393 hr = device->QueryInterface((ID3D10Device1**)byRef(mDevice));
1395 if (FAILED(hr)) {
1396 gfxWarning() << "Failed to get D3D10 device from texture.";
1397 return false;
1398 }
1400 D3D10_TEXTURE2D_DESC desc;
1401 mTexture->GetDesc(&desc);
1402 mSize.width = desc.Width;
1403 mSize.height = desc.Height;
1405 return InitD2DRenderTarget();
1406 }
1408 // {0D398B49-AE7B-416F-B26D-EA3C137D1CF7}
1409 static const GUID sPrivateDataD2D =
1410 { 0xd398b49, 0xae7b, 0x416f, { 0xb2, 0x6d, 0xea, 0x3c, 0x13, 0x7d, 0x1c, 0xf7 } };
1412 bool
1413 DrawTargetD2D::InitD3D10Data()
1414 {
1415 HRESULT hr;
1417 UINT privateDataSize;
1418 privateDataSize = sizeof(mPrivateData);
1419 hr = mDevice->GetPrivateData(sPrivateDataD2D, &privateDataSize, &mPrivateData);
1421 if (SUCCEEDED(hr)) {
1422 return true;
1423 }
1425 mPrivateData = new PrivateD3D10DataD2D;
1427 decltype(D3D10CreateEffectFromMemory)* createD3DEffect;
1428 HMODULE d3dModule = LoadLibraryW(L"d3d10_1.dll");
1429 createD3DEffect = (decltype(D3D10CreateEffectFromMemory)*)
1430 GetProcAddress(d3dModule, "D3D10CreateEffectFromMemory");
1432 hr = createD3DEffect((void*)d2deffect, sizeof(d2deffect), 0, mDevice, nullptr, byRef(mPrivateData->mEffect));
1434 if (FAILED(hr)) {
1435 gfxWarning() << "Failed to initialize Direct2D required effects. Code: " << hr;
1436 return false;
1437 }
1439 privateDataSize = sizeof(mPrivateData);
1440 mDevice->SetPrivateData(sPrivateDataD2D, privateDataSize, &mPrivateData);
1442 D3D10_INPUT_ELEMENT_DESC layout[] =
1443 {
1444 { "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
1445 };
1446 D3D10_PASS_DESC passDesc;
1448 mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->GetDesc(&passDesc);
1450 hr = mDevice->CreateInputLayout(layout,
1451 sizeof(layout) / sizeof(D3D10_INPUT_ELEMENT_DESC),
1452 passDesc.pIAInputSignature,
1453 passDesc.IAInputSignatureSize,
1454 byRef(mPrivateData->mInputLayout));
1456 if (FAILED(hr)) {
1457 gfxWarning() << "Failed to initialize Direct2D required InputLayout. Code: " << hr;
1458 return false;
1459 }
1461 D3D10_SUBRESOURCE_DATA data;
1462 Vertex vertices[] = { {0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0} };
1463 data.pSysMem = vertices;
1464 CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER);
1466 hr = mDevice->CreateBuffer(&bufferDesc, &data, byRef(mPrivateData->mVB));
1468 if (FAILED(hr)) {
1469 gfxWarning() << "Failed to initialize Direct2D required VertexBuffer. Code: " << hr;
1470 return false;
1471 }
1473 return true;
1474 }
1476 /*
1477 * Private helpers
1478 */
1479 uint32_t
1480 DrawTargetD2D::GetByteSize() const
1481 {
1482 return mSize.width * mSize.height * BytesPerPixel(mFormat);
1483 }
1485 TemporaryRef<ID2D1Layer>
1486 DrawTargetD2D::GetCachedLayer()
1487 {
1488 RefPtr<ID2D1Layer> layer;
1490 if (mCurrentCachedLayer < 5) {
1491 if (!mCachedLayers[mCurrentCachedLayer]) {
1492 mRT->CreateLayer(byRef(mCachedLayers[mCurrentCachedLayer]));
1493 mVRAMUsageDT += GetByteSize();
1494 }
1495 layer = mCachedLayers[mCurrentCachedLayer];
1496 } else {
1497 mRT->CreateLayer(byRef(layer));
1498 }
1500 mCurrentCachedLayer++;
1501 return layer;
1502 }
1504 void
1505 DrawTargetD2D::PopCachedLayer(ID2D1RenderTarget *aRT)
1506 {
1507 aRT->PopLayer();
1508 mCurrentCachedLayer--;
1509 }
1511 bool
1512 DrawTargetD2D::InitD2DRenderTarget()
1513 {
1514 if (!factory()) {
1515 return false;
1516 }
1518 mRT = CreateRTForTexture(mTexture, mFormat);
1520 if (!mRT) {
1521 return false;
1522 }
1524 mRT->BeginDraw();
1526 if (mFormat == SurfaceFormat::B8G8R8X8) {
1527 mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE);
1528 }
1530 mVRAMUsageDT += GetByteSize();
1532 return InitD3D10Data();
1533 }
1535 void
1536 DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT)
1537 {
1538 if (!mClipsArePushed || aRT == mTempRT) {
1539 if (mPushedClips.size()) {
1540 // The transform of clips is relative to the world matrix, since we use the total
1541 // transform for the clips, make the world matrix identity.
1542 aRT->SetTransform(D2D1::IdentityMatrix());
1543 if (aRT == mRT) {
1544 mTransformDirty = true;
1545 mClipsArePushed = true;
1546 }
1547 PushClipsToRT(aRT);
1548 }
1549 }
1550 FlushTransformToRT();
1551 MarkChanged();
1553 if (aRT == mTempRT) {
1554 mTempRT->SetTransform(D2DMatrix(mTransform));
1555 }
1556 }
1558 void
1559 DrawTargetD2D::MarkChanged()
1560 {
1561 if (mSnapshot) {
1562 if (mSnapshot->hasOneRef()) {
1563 // Just destroy it, since no-one else knows about it.
1564 mSnapshot = nullptr;
1565 } else {
1566 mSnapshot->DrawTargetWillChange();
1567 // The snapshot will no longer depend on this target.
1568 MOZ_ASSERT(!mSnapshot);
1569 }
1570 }
1571 if (mDependentTargets.size()) {
1572 // Copy mDependentTargets since the Flush()es below will modify it.
1573 TargetSet tmpTargets = mDependentTargets;
1574 for (TargetSet::iterator iter = tmpTargets.begin();
1575 iter != tmpTargets.end(); iter++) {
1576 (*iter)->Flush();
1577 }
1578 // The Flush() should have broken all dependencies on this target.
1579 MOZ_ASSERT(!mDependentTargets.size());
1580 }
1581 }
1583 ID3D10BlendState*
1584 DrawTargetD2D::GetBlendStateForOperator(CompositionOp aOperator)
1585 {
1586 size_t operatorIndex = static_cast<size_t>(aOperator);
1587 if (mPrivateData->mBlendStates[operatorIndex]) {
1588 return mPrivateData->mBlendStates[operatorIndex];
1589 }
1591 D3D10_BLEND_DESC desc;
1593 memset(&desc, 0, sizeof(D3D10_BLEND_DESC));
1595 desc.AlphaToCoverageEnable = FALSE;
1596 desc.BlendEnable[0] = TRUE;
1597 desc.RenderTargetWriteMask[0] = D3D10_COLOR_WRITE_ENABLE_ALL;
1598 desc.BlendOp = desc.BlendOpAlpha = D3D10_BLEND_OP_ADD;
1600 switch (aOperator) {
1601 case CompositionOp::OP_ADD:
1602 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
1603 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
1604 break;
1605 case CompositionOp::OP_IN:
1606 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
1607 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
1608 break;
1609 case CompositionOp::OP_OUT:
1610 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
1611 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
1612 break;
1613 case CompositionOp::OP_ATOP:
1614 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_DEST_ALPHA;
1615 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
1616 break;
1617 case CompositionOp::OP_DEST_IN:
1618 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
1619 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
1620 break;
1621 case CompositionOp::OP_DEST_OUT:
1622 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ZERO;
1623 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
1624 break;
1625 case CompositionOp::OP_DEST_ATOP:
1626 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
1627 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_SRC_ALPHA;
1628 break;
1629 case CompositionOp::OP_DEST_OVER:
1630 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
1631 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ONE;
1632 break;
1633 case CompositionOp::OP_XOR:
1634 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_INV_DEST_ALPHA;
1635 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
1636 break;
1637 case CompositionOp::OP_SOURCE:
1638 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
1639 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_ZERO;
1640 break;
1641 default:
1642 desc.SrcBlend = desc.SrcBlendAlpha = D3D10_BLEND_ONE;
1643 desc.DestBlend = desc.DestBlendAlpha = D3D10_BLEND_INV_SRC_ALPHA;
1644 }
1646 mDevice->CreateBlendState(&desc, byRef(mPrivateData->mBlendStates[operatorIndex]));
1648 return mPrivateData->mBlendStates[operatorIndex];
1649 }
1651 /* This function prepares the temporary RT for drawing and returns it when a
1652 * drawing operation other than OVER is required.
1653 */
1654 ID2D1RenderTarget*
1655 DrawTargetD2D::GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern)
1656 {
1657 if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
1658 return mRT;
1659 }
1661 PopAllClips();
1663 if (aOperator > CompositionOp::OP_XOR) {
1664 mRT->Flush();
1665 }
1667 if (mTempRT) {
1668 mTempRT->Clear(D2D1::ColorF(0, 0));
1669 return mTempRT;
1670 }
1672 EnsureViews();
1674 if (!mRTView || !mSRView) {
1675 gfxDebug() << *this << ": Failed to get required views. Defaulting to CompositionOp::OP_OVER.";
1676 return mRT;
1677 }
1679 mTempRT = CreateRTForTexture(mTempTexture, SurfaceFormat::B8G8R8A8);
1681 if (!mTempRT) {
1682 return mRT;
1683 }
1685 mVRAMUsageDT += GetByteSize();
1687 mTempRT->BeginDraw();
1689 mTempRT->Clear(D2D1::ColorF(0, 0));
1691 return mTempRT;
1692 }
1694 /* This function blends back the content of a drawing operation (drawn to an
1695 * empty surface with OVER, so the surface now contains the source operation
1696 * contents) to the rendertarget using the requested composition operation.
1697 * In order to respect clip for operations which are unbound by their mask,
1698 * the old content of the surface outside the clipped area may be blended back
1699 * to the surface.
1700 */
1701 void
1702 DrawTargetD2D::FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds)
1703 {
1704 if (aOperator == CompositionOp::OP_OVER && IsPatternSupportedByD2D(aPattern)) {
1705 return;
1706 }
1708 if (!mTempRT) {
1709 return;
1710 }
1712 PopClipsFromRT(mTempRT);
1714 mRT->Flush();
1715 mTempRT->Flush();
1717 AutoSaveRestoreClippedOut restoreClippedOut(this);
1719 bool needsWriteBack =
1720 !IsOperatorBoundByMask(aOperator) && mPushedClips.size();
1722 if (needsWriteBack) {
1723 restoreClippedOut.Save();
1724 }
1726 ID3D10RenderTargetView *rtViews = mRTView;
1727 mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
1729 UINT stride = sizeof(Vertex);
1730 UINT offset = 0;
1731 ID3D10Buffer *buff = mPrivateData->mVB;
1733 mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
1734 mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
1735 mDevice->IASetInputLayout(mPrivateData->mInputLayout);
1737 D3D10_VIEWPORT viewport;
1738 viewport.MaxDepth = 1;
1739 viewport.MinDepth = 0;
1740 viewport.Height = mSize.height;
1741 viewport.Width = mSize.width;
1742 viewport.TopLeftX = 0;
1743 viewport.TopLeftY = 0;
1745 RefPtr<ID3D10Texture2D> tmpTexture;
1746 RefPtr<ID3D10ShaderResourceView> mBckSRView;
1748 mDevice->RSSetViewports(1, &viewport);
1749 mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
1750 SetFloatVector(ShaderConstantRectD3D10(-1.0f, 1.0f, 2.0f, -2.0f));
1752 if (IsPatternSupportedByD2D(aPattern)) {
1753 mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
1754 SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
1755 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(mSRView);
1757 // Handle the case where we blend with the backdrop
1758 if (aOperator > CompositionOp::OP_XOR) {
1759 IntSize size = mSize;
1760 SurfaceFormat format = mFormat;
1762 CD3D10_TEXTURE2D_DESC desc(DXGIFormat(format), size.width, size.height, 1, 1);
1763 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
1765 HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(tmpTexture));
1766 if (FAILED(hr)) {
1767 gfxWarning() << "Failed to create temporary texture to hold surface data.";
1768 return;
1769 }
1771 mDevice->CopyResource(tmpTexture, mTexture);
1772 if (FAILED(hr)) {
1773 gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
1774 return;
1775 }
1777 DrawTargetD2D::Flush();
1779 hr = mDevice->CreateShaderResourceView(tmpTexture, nullptr, byRef(mBckSRView));
1781 if (FAILED(hr)) {
1782 gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
1783 return;
1784 }
1786 unsigned int compop = (unsigned int)aOperator - (unsigned int)CompositionOp::OP_XOR;
1787 mPrivateData->mEffect->GetVariableByName("bcktex")->AsShaderResource()->SetResource(mBckSRView);
1788 mPrivateData->mEffect->GetVariableByName("blendop")->AsScalar()->SetInt(compop);
1790 if (aOperator > CompositionOp::OP_EXCLUSION)
1791 mPrivateData->mEffect->GetTechniqueByName("SampleTextureForNonSeparableBlending")->
1792 GetPassByIndex(0)->Apply(0);
1793 else if (aOperator > CompositionOp::OP_COLOR_DODGE)
1794 mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_2")->
1795 GetPassByIndex(0)->Apply(0);
1796 else
1797 mPrivateData->mEffect->GetTechniqueByName("SampleTextureForSeparableBlending_1")->
1798 GetPassByIndex(0)->Apply(0);
1799 }
1800 else {
1801 mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->GetPassByIndex(0)->Apply(0);
1802 }
1804 } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
1805 const RadialGradientPattern *pat = static_cast<const RadialGradientPattern*>(&aPattern);
1807 if (pat->mCenter1 == pat->mCenter2 && pat->mRadius1 == pat->mRadius2) {
1808 // Draw nothing!
1809 return;
1810 }
1812 mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(mSRView);
1814 SetupEffectForRadialGradient(pat);
1815 }
1817 mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
1819 SetScissorToRect(nullptr);
1820 mDevice->Draw(4, 0);
1821 }
1823 static D2D1_RECT_F
1824 IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
1825 {
1826 D2D1_RECT_F result;
1827 result.left = max(aRect1.left, aRect2.left);
1828 result.top = max(aRect1.top, aRect2.top);
1829 result.right = min(aRect1.right, aRect2.right);
1830 result.bottom = min(aRect1.bottom, aRect2.bottom);
1832 result.right = max(result.right, result.left);
1833 result.bottom = max(result.bottom, result.top);
1835 return result;
1836 }
1838 bool
1839 DrawTargetD2D::GetDeviceSpaceClipRect(D2D1_RECT_F& aClipRect, bool& aIsPixelAligned)
1840 {
1841 if (!mPushedClips.size()) {
1842 return false;
1843 }
1845 std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
1846 if (iter->mPath) {
1847 return false;
1848 }
1849 aClipRect = iter->mBounds;
1850 aIsPixelAligned = iter->mIsPixelAligned;
1852 iter++;
1853 for (;iter != mPushedClips.end(); iter++) {
1854 if (iter->mPath) {
1855 return false;
1856 }
1857 aClipRect = IntersectRect(aClipRect, iter->mBounds);
1858 if (!iter->mIsPixelAligned) {
1859 aIsPixelAligned = false;
1860 }
1861 }
1862 return true;
1863 }
1865 TemporaryRef<ID2D1Geometry>
1866 DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds)
1867 {
1868 if (mCurrentClippedGeometry) {
1869 *aClipBounds = mCurrentClipBounds;
1870 return mCurrentClippedGeometry;
1871 }
1873 mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
1875 // if pathGeom is null then pathRect represents the path.
1876 RefPtr<ID2D1Geometry> pathGeom;
1877 D2D1_RECT_F pathRect;
1878 bool pathRectIsAxisAligned = false;
1879 std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
1881 if (iter->mPath) {
1882 pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
1883 } else {
1884 pathRect = iter->mBounds;
1885 pathRectIsAxisAligned = iter->mIsPixelAligned;
1886 }
1888 iter++;
1889 for (;iter != mPushedClips.end(); iter++) {
1890 // Do nothing but add it to the current clip bounds.
1891 if (!iter->mPath && iter->mIsPixelAligned) {
1892 mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
1893 IntRect(int32_t(iter->mBounds.left), int32_t(iter->mBounds.top),
1894 int32_t(iter->mBounds.right - iter->mBounds.left),
1895 int32_t(iter->mBounds.bottom - iter->mBounds.top)));
1896 continue;
1897 }
1899 if (!pathGeom) {
1900 if (pathRectIsAxisAligned) {
1901 mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
1902 IntRect(int32_t(pathRect.left), int32_t(pathRect.top),
1903 int32_t(pathRect.right - pathRect.left),
1904 int32_t(pathRect.bottom - pathRect.top)));
1905 }
1906 if (iter->mPath) {
1907 // See if pathRect needs to go into the path geometry.
1908 if (!pathRectIsAxisAligned) {
1909 pathGeom = ConvertRectToGeometry(pathRect);
1910 } else {
1911 pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
1912 }
1913 } else {
1914 pathRect = IntersectRect(pathRect, iter->mBounds);
1915 pathRectIsAxisAligned = false;
1916 continue;
1917 }
1918 }
1920 RefPtr<ID2D1PathGeometry> newGeom;
1921 factory()->CreatePathGeometry(byRef(newGeom));
1923 RefPtr<ID2D1GeometrySink> currentSink;
1924 newGeom->Open(byRef(currentSink));
1926 if (iter->mPath) {
1927 pathGeom->CombineWithGeometry(iter->mPath->GetGeometry(), D2D1_COMBINE_MODE_INTERSECT,
1928 iter->mTransform, currentSink);
1929 } else {
1930 RefPtr<ID2D1Geometry> rectGeom = ConvertRectToGeometry(iter->mBounds);
1931 pathGeom->CombineWithGeometry(rectGeom, D2D1_COMBINE_MODE_INTERSECT,
1932 D2D1::IdentityMatrix(), currentSink);
1933 }
1935 currentSink->Close();
1937 pathGeom = newGeom.forget();
1938 }
1940 // For now we need mCurrentClippedGeometry to always be non-nullptr. This
1941 // method might seem a little strange but it is just fine, if pathGeom is
1942 // nullptr pathRect will always still contain 1 clip unaccounted for
1943 // regardless of mCurrentClipBounds.
1944 if (!pathGeom) {
1945 pathGeom = ConvertRectToGeometry(pathRect);
1946 }
1947 mCurrentClippedGeometry = pathGeom.forget();
1948 *aClipBounds = mCurrentClipBounds;
1949 return mCurrentClippedGeometry;
1950 }
1952 TemporaryRef<ID2D1RenderTarget>
1953 DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
1954 {
1955 HRESULT hr;
1957 RefPtr<IDXGISurface> surface;
1958 RefPtr<ID2D1RenderTarget> rt;
1960 hr = aTexture->QueryInterface((IDXGISurface**)byRef(surface));
1962 if (FAILED(hr)) {
1963 gfxWarning() << "Failed to QI texture to surface.";
1964 return nullptr;
1965 }
1967 D3D10_TEXTURE2D_DESC desc;
1968 aTexture->GetDesc(&desc);
1970 D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED;
1972 if (aFormat == SurfaceFormat::B8G8R8X8 && aTexture == mTexture) {
1973 alphaMode = D2D1_ALPHA_MODE_IGNORE;
1974 }
1976 D2D1_RENDER_TARGET_PROPERTIES props =
1977 D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode));
1978 hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt));
1980 if (FAILED(hr)) {
1981 gfxWarning() << "Failed to create D2D render target for texture.";
1982 return nullptr;
1983 }
1985 return rt;
1986 }
1988 void
1989 DrawTargetD2D::EnsureViews()
1990 {
1991 if (mTempTexture && mSRView && mRTView) {
1992 return;
1993 }
1995 HRESULT hr;
1997 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
1998 mSize.width,
1999 mSize.height,
2000 1, 1);
2001 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
2003 hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mTempTexture));
2005 if (FAILED(hr)) {
2006 gfxWarning() << *this << "Failed to create temporary texture for rendertarget. Size: "
2007 << mSize << " Code: " << hr;
2008 return;
2009 }
2011 hr = mDevice->CreateShaderResourceView(mTempTexture, nullptr, byRef(mSRView));
2013 if (FAILED(hr)) {
2014 gfxWarning() << *this << "Failed to create shader resource view for temp texture. Code: " << hr;
2015 return;
2016 }
2018 hr = mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(mRTView));
2020 if (FAILED(hr)) {
2021 gfxWarning() << *this << "Failed to create rendertarget view for temp texture. Code: " << hr;
2022 }
2023 }
2025 void
2026 DrawTargetD2D::PopAllClips()
2027 {
2028 if (mClipsArePushed) {
2029 PopClipsFromRT(mRT);
2031 mClipsArePushed = false;
2032 }
2033 }
2035 void
2036 DrawTargetD2D::PushClipsToRT(ID2D1RenderTarget *aRT)
2037 {
2038 for (std::vector<PushedClip>::iterator iter = mPushedClips.begin();
2039 iter != mPushedClips.end(); iter++) {
2040 if (iter->mLayer) {
2041 PushD2DLayer(aRT, iter->mPath->mGeometry, iter->mLayer, iter->mTransform);
2042 } else {
2043 aRT->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
2044 }
2045 }
2046 }
2048 void
2049 DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT)
2050 {
2051 for (int i = mPushedClips.size() - 1; i >= 0; i--) {
2052 if (mPushedClips[i].mLayer) {
2053 aRT->PopLayer();
2054 } else {
2055 aRT->PopAxisAlignedClip();
2056 }
2057 }
2058 }
2060 void
2061 DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds)
2062 {
2063 if (mCurrentClipMaskTexture || mPushedClips.empty()) {
2064 *aBounds = mCurrentClipBounds;
2065 return;
2066 }
2068 RefPtr<ID2D1Geometry> geometry = GetClippedGeometry(aBounds);
2070 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM,
2071 mSize.width,
2072 mSize.height,
2073 1, 1);
2074 desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
2076 HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mCurrentClipMaskTexture));
2078 if (FAILED(hr)) {
2079 gfxWarning() << "Failed to create texture for ClipMask!";
2080 return;
2081 }
2083 RefPtr<ID2D1RenderTarget> rt = CreateRTForTexture(mCurrentClipMaskTexture, SurfaceFormat::A8);
2085 if (!rt) {
2086 gfxWarning() << "Failed to create RT for ClipMask!";
2087 return;
2088 }
2090 RefPtr<ID2D1SolidColorBrush> brush;
2091 rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
2093 rt->BeginDraw();
2094 rt->Clear(D2D1::ColorF(0, 0));
2095 rt->FillGeometry(geometry, brush);
2096 rt->EndDraw();
2097 }
2099 bool
2100 DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont,
2101 const GlyphBuffer &aBuffer,
2102 const Color &aColor,
2103 IDWriteRenderingParams *aParams,
2104 const DrawOptions &aOptions)
2105 {
2106 HRESULT hr;
2108 RefPtr<IDWriteRenderingParams> params;
2110 if (aParams) {
2111 params = aParams;
2112 } else {
2113 mRT->GetTextRenderingParams(byRef(params));
2114 }
2116 DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT;
2117 if (params) {
2118 hr = aFont->mFontFace->GetRecommendedRenderingMode(
2119 (FLOAT)aFont->GetSize(),
2120 1.0f,
2121 DWRITE_MEASURING_MODE_NATURAL,
2122 params,
2123 &renderMode);
2124 if (FAILED(hr)) {
2125 // this probably never happens, but let's play it safe
2126 renderMode = DWRITE_RENDERING_MODE_DEFAULT;
2127 }
2128 }
2130 // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept.
2131 switch (renderMode) {
2132 case DWRITE_RENDERING_MODE_ALIASED:
2133 // ClearType texture creation will fail in this mode, so bail out
2134 return false;
2135 case DWRITE_RENDERING_MODE_DEFAULT:
2136 // As per DWRITE_RENDERING_MODE documentation, pick Natural for font
2137 // sizes under 16 ppem
2138 if (aFont->GetSize() < 16.0f) {
2139 renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
2140 } else {
2141 renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
2142 }
2143 break;
2144 case DWRITE_RENDERING_MODE_OUTLINE:
2145 renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
2146 break;
2147 default:
2148 break;
2149 }
2151 DWRITE_MEASURING_MODE measureMode =
2152 renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
2153 renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
2154 DWRITE_MEASURING_MODE_NATURAL;
2156 DWRITE_MATRIX mat = DWriteMatrixFromMatrix(mTransform);
2158 AutoDWriteGlyphRun autoRun;
2159 DWriteGlyphRunFromGlyphs(aBuffer, aFont, &autoRun);
2161 RefPtr<IDWriteGlyphRunAnalysis> analysis;
2162 hr = GetDWriteFactory()->CreateGlyphRunAnalysis(&autoRun, 1.0f, &mat,
2163 renderMode, measureMode, 0, 0, byRef(analysis));
2165 if (FAILED(hr)) {
2166 return false;
2167 }
2169 RECT bounds;
2170 hr = analysis->GetAlphaTextureBounds(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds);
2172 if (bounds.bottom <= bounds.top || bounds.right <= bounds.left) {
2173 // DWrite seems to do this sometimes. I'm not 100% sure why. See bug 758980.
2174 gfxDebug() << "Empty alpha texture bounds! Falling back to regular drawing.";
2175 return false;
2176 }
2177 IntRect rectBounds(bounds.left, bounds.top, bounds.right - bounds.left, bounds.bottom - bounds.top);
2178 IntRect surfBounds(IntPoint(0, 0), mSize);
2180 rectBounds.IntersectRect(rectBounds, surfBounds);
2182 if (rectBounds.IsEmpty()) {
2183 // Nothing to do.
2184 return true;
2185 }
2187 RefPtr<ID3D10Texture2D> tex = CreateTextureForAnalysis(analysis, rectBounds);
2189 if (!tex) {
2190 return false;
2191 }
2193 RefPtr<ID3D10ShaderResourceView> srView;
2194 hr = mDevice->CreateShaderResourceView(tex, nullptr, byRef(srView));
2196 if (FAILED(hr)) {
2197 return false;
2198 }
2200 MarkChanged();
2202 // Prepare our background texture for drawing.
2203 PopAllClips();
2204 mRT->Flush();
2206 SetupStateForRendering();
2208 ID3D10EffectTechnique *technique = mPrivateData->mEffect->GetTechniqueByName("SampleTextTexture");
2210 mPrivateData->mEffect->GetVariableByName("QuadDesc")->AsVector()->
2211 SetFloatVector(ShaderConstantRectD3D10(-1.0f + ((Float(rectBounds.x) / mSize.width) * 2.0f),
2212 1.0f - (Float(rectBounds.y) / mSize.height * 2.0f),
2213 (Float(rectBounds.width) / mSize.width) * 2.0f,
2214 (-Float(rectBounds.height) / mSize.height) * 2.0f));
2215 mPrivateData->mEffect->GetVariableByName("TexCoords")->AsVector()->
2216 SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
2217 FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a };
2218 mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()->
2219 SetFloatVector(color);
2221 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
2223 bool isMasking = false;
2225 IntRect clipBoundsStorage;
2226 IntRect *clipBounds = nullptr;
2228 if (!mPushedClips.empty()) {
2229 clipBounds = &clipBoundsStorage;
2230 RefPtr<ID2D1Geometry> geom = GetClippedGeometry(clipBounds);
2232 RefPtr<ID2D1RectangleGeometry> rectGeom;
2233 factory()->CreateRectangleGeometry(D2D1::RectF(Float(rectBounds.x),
2234 Float(rectBounds.y),
2235 Float(rectBounds.width + rectBounds.x),
2236 Float(rectBounds.height + rectBounds.y)),
2237 byRef(rectGeom));
2239 D2D1_GEOMETRY_RELATION relation;
2240 if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) ||
2241 relation != D2D1_GEOMETRY_RELATION_CONTAINS ) {
2242 isMasking = true;
2243 }
2244 }
2246 if (isMasking) {
2247 clipBounds = &clipBoundsStorage;
2248 EnsureClipMaskTexture(clipBounds);
2250 RefPtr<ID3D10ShaderResourceView> srViewMask;
2251 hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(srViewMask));
2253 if (FAILED(hr)) {
2254 return false;
2255 }
2257 mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(srViewMask);
2259 mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
2260 SetFloatVector(ShaderConstantRectD3D10(Float(rectBounds.x) / mSize.width, Float(rectBounds.y) / mSize.height,
2261 Float(rectBounds.width) / mSize.width, Float(rectBounds.height) / mSize.height));
2263 technique->GetPassByIndex(1)->Apply(0);
2264 } else {
2265 technique->GetPassByIndex(0)->Apply(0);
2266 }
2268 RefPtr<ID3D10RenderTargetView> rtView;
2269 ID3D10RenderTargetView *rtViews;
2270 mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(rtView));
2272 rtViews = rtView;
2273 mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
2274 SetScissorToRect(clipBounds);
2275 mDevice->Draw(4, 0);
2276 return true;
2277 }
2279 TemporaryRef<ID2D1Brush>
2280 DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
2281 {
2282 if (!IsPatternSupportedByD2D(aPattern)) {
2283 RefPtr<ID2D1SolidColorBrush> colBrush;
2284 mRT->CreateSolidColorBrush(D2D1::ColorF(1.0f, 1.0f, 1.0f, 1.0f), byRef(colBrush));
2285 return colBrush;
2286 }
2288 if (aPattern.GetType() == PatternType::COLOR) {
2289 RefPtr<ID2D1SolidColorBrush> colBrush;
2290 Color color = static_cast<const ColorPattern*>(&aPattern)->mColor;
2291 mRT->CreateSolidColorBrush(D2D1::ColorF(color.r, color.g,
2292 color.b, color.a),
2293 D2D1::BrushProperties(aAlpha),
2294 byRef(colBrush));
2295 return colBrush;
2296 } else if (aPattern.GetType() == PatternType::LINEAR_GRADIENT) {
2297 RefPtr<ID2D1LinearGradientBrush> gradBrush;
2298 const LinearGradientPattern *pat =
2299 static_cast<const LinearGradientPattern*>(&aPattern);
2301 GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
2303 if (!stops) {
2304 gfxDebug() << "No stops specified for gradient pattern.";
2305 return nullptr;
2306 }
2308 if (pat->mBegin == pat->mEnd) {
2309 RefPtr<ID2D1SolidColorBrush> colBrush;
2310 uint32_t stopCount = stops->mStopCollection->GetGradientStopCount();
2311 vector<D2D1_GRADIENT_STOP> d2dStops(stopCount);
2312 stops->mStopCollection->GetGradientStops(&d2dStops.front(), stopCount);
2313 mRT->CreateSolidColorBrush(d2dStops.back().color,
2314 D2D1::BrushProperties(aAlpha),
2315 byRef(colBrush));
2316 return colBrush;
2317 }
2319 mRT->CreateLinearGradientBrush(D2D1::LinearGradientBrushProperties(D2DPoint(pat->mBegin),
2320 D2DPoint(pat->mEnd)),
2321 D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
2322 stops->mStopCollection,
2323 byRef(gradBrush));
2324 return gradBrush;
2325 } else if (aPattern.GetType() == PatternType::RADIAL_GRADIENT) {
2326 RefPtr<ID2D1RadialGradientBrush> gradBrush;
2327 const RadialGradientPattern *pat =
2328 static_cast<const RadialGradientPattern*>(&aPattern);
2330 GradientStopsD2D *stops = static_cast<GradientStopsD2D*>(pat->mStops.get());
2332 if (!stops) {
2333 gfxDebug() << "No stops specified for gradient pattern.";
2334 return nullptr;
2335 }
2337 // This will not be a complex radial gradient brush.
2338 mRT->CreateRadialGradientBrush(
2339 D2D1::RadialGradientBrushProperties(D2DPoint(pat->mCenter2),
2340 D2DPoint(pat->mCenter1 - pat->mCenter2),
2341 pat->mRadius2, pat->mRadius2),
2342 D2D1::BrushProperties(aAlpha, D2DMatrix(pat->mMatrix)),
2343 stops->mStopCollection,
2344 byRef(gradBrush));
2346 return gradBrush;
2347 } else if (aPattern.GetType() == PatternType::SURFACE) {
2348 RefPtr<ID2D1BitmapBrush> bmBrush;
2349 const SurfacePattern *pat =
2350 static_cast<const SurfacePattern*>(&aPattern);
2352 if (!pat->mSurface) {
2353 gfxDebug() << "No source surface specified for surface pattern";
2354 return nullptr;
2355 }
2357 RefPtr<ID2D1Bitmap> bitmap;
2359 Matrix mat = pat->mMatrix;
2361 switch (pat->mSurface->GetType()) {
2362 case SurfaceType::D2D1_BITMAP:
2363 {
2364 SourceSurfaceD2D *surf = static_cast<SourceSurfaceD2D*>(pat->mSurface.get());
2366 bitmap = surf->mBitmap;
2368 if (!bitmap) {
2369 return nullptr;
2370 }
2371 }
2372 break;
2373 case SurfaceType::D2D1_DRAWTARGET:
2374 {
2375 SourceSurfaceD2DTarget *surf =
2376 static_cast<SourceSurfaceD2DTarget*>(pat->mSurface.get());
2377 bitmap = surf->GetBitmap(mRT);
2378 AddDependencyOnSource(surf);
2379 }
2380 break;
2381 default:
2382 {
2383 RefPtr<DataSourceSurface> dataSurf = pat->mSurface->GetDataSurface();
2384 if (!dataSurf) {
2385 gfxWarning() << "Invalid surface type.";
2386 return nullptr;
2387 }
2389 bitmap = CreatePartialBitmapForSurface(dataSurf, mTransform, mSize, pat->mExtendMode, mat, mRT);
2390 if (!bitmap) {
2391 return nullptr;
2392 }
2393 }
2394 break;
2395 }
2397 mRT->CreateBitmapBrush(bitmap,
2398 D2D1::BitmapBrushProperties(D2DExtend(pat->mExtendMode),
2399 D2DExtend(pat->mExtendMode),
2400 D2DFilter(pat->mFilter)),
2401 D2D1::BrushProperties(aAlpha, D2DMatrix(mat)),
2402 byRef(bmBrush));
2404 return bmBrush;
2405 }
2407 gfxWarning() << "Invalid pattern type detected.";
2408 return nullptr;
2409 }
2411 TemporaryRef<ID3D10Texture2D>
2412 DrawTargetD2D::CreateGradientTexture(const GradientStopsD2D *aStops)
2413 {
2414 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM, 4096, 1, 1, 1);
2416 std::vector<D2D1_GRADIENT_STOP> rawStops;
2417 rawStops.resize(aStops->mStopCollection->GetGradientStopCount());
2418 aStops->mStopCollection->GetGradientStops(&rawStops.front(), rawStops.size());
2420 std::vector<unsigned char> textureData;
2421 textureData.resize(4096 * 4);
2422 unsigned char *texData = &textureData.front();
2424 float prevColorPos = 0;
2425 float nextColorPos = 1.0f;
2426 D2D1_COLOR_F prevColor = rawStops[0].color;
2427 D2D1_COLOR_F nextColor = prevColor;
2429 if (rawStops.size() >= 2) {
2430 nextColor = rawStops[1].color;
2431 nextColorPos = rawStops[1].position;
2432 }
2434 uint32_t stopPosition = 2;
2436 // Not the most optimized way but this will do for now.
2437 for (int i = 0; i < 4096; i++) {
2438 // The 4095 seems a little counter intuitive, but we want the gradient
2439 // color at offset 0 at the first pixel, and at offset 1.0f at the last
2440 // pixel.
2441 float pos = float(i) / 4095;
2443 while (pos > nextColorPos) {
2444 prevColor = nextColor;
2445 prevColorPos = nextColorPos;
2446 if (rawStops.size() > stopPosition) {
2447 nextColor = rawStops[stopPosition].color;
2448 nextColorPos = rawStops[stopPosition++].position;
2449 } else {
2450 nextColorPos = 1.0f;
2451 }
2452 }
2454 float interp;
2456 if (nextColorPos != prevColorPos) {
2457 interp = (pos - prevColorPos) / (nextColorPos - prevColorPos);
2458 } else {
2459 interp = 0;
2460 }
2462 Color newColor(prevColor.r + (nextColor.r - prevColor.r) * interp,
2463 prevColor.g + (nextColor.g - prevColor.g) * interp,
2464 prevColor.b + (nextColor.b - prevColor.b) * interp,
2465 prevColor.a + (nextColor.a - prevColor.a) * interp);
2467 texData[i * 4] = (char)(255.0f * newColor.b);
2468 texData[i * 4 + 1] = (char)(255.0f * newColor.g);
2469 texData[i * 4 + 2] = (char)(255.0f * newColor.r);
2470 texData[i * 4 + 3] = (char)(255.0f * newColor.a);
2471 }
2473 D3D10_SUBRESOURCE_DATA data;
2474 data.pSysMem = &textureData.front();
2475 data.SysMemPitch = 4096 * 4;
2477 RefPtr<ID3D10Texture2D> tex;
2478 mDevice->CreateTexture2D(&desc, &data, byRef(tex));
2480 return tex;
2481 }
2483 TemporaryRef<ID3D10Texture2D>
2484 DrawTargetD2D::CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds)
2485 {
2486 HRESULT hr;
2488 uint32_t bufferSize = aBounds.width * aBounds.height * 3;
2490 RECT bounds;
2491 bounds.left = aBounds.x;
2492 bounds.top = aBounds.y;
2493 bounds.right = aBounds.x + aBounds.width;
2494 bounds.bottom = aBounds.y + aBounds.height;
2496 // Add one byte so we can safely read a 32-bit int when copying the last
2497 // 3 bytes.
2498 BYTE *texture = new BYTE[bufferSize + 1];
2499 hr = aAnalysis->CreateAlphaTexture(DWRITE_TEXTURE_CLEARTYPE_3x1, &bounds, texture, bufferSize);
2501 if (FAILED(hr)) {
2502 delete [] texture;
2503 return nullptr;
2504 }
2506 int alignedBufferSize = aBounds.width * aBounds.height * 4;
2508 // Create a one-off immutable texture from system memory.
2509 BYTE *alignedTextureData = new BYTE[alignedBufferSize];
2510 for (int y = 0; y < aBounds.height; y++) {
2511 for (int x = 0; x < aBounds.width; x++) {
2512 // Copy 3 Bpp source to 4 Bpp destination memory used for
2513 // texture creation. D3D10 has no 3 Bpp texture format we can
2514 // use.
2515 //
2516 // Since we don't care what ends up in the alpha pixel of the
2517 // destination, therefor we can simply copy a normal 32 bit
2518 // integer each time, filling the alpha pixel of the destination
2519 // with the first subpixel of the next pixel from the source.
2520 *((int*)(alignedTextureData + (y * aBounds.width + x) * 4)) =
2521 *((int*)(texture + (y * aBounds.width + x) * 3));
2522 }
2523 }
2525 D3D10_SUBRESOURCE_DATA data;
2527 CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_B8G8R8A8_UNORM,
2528 aBounds.width, aBounds.height,
2529 1, 1);
2530 desc.Usage = D3D10_USAGE_IMMUTABLE;
2532 data.SysMemPitch = aBounds.width * 4;
2533 data.pSysMem = alignedTextureData;
2535 RefPtr<ID3D10Texture2D> tex;
2536 hr = mDevice->CreateTexture2D(&desc, &data, byRef(tex));
2538 delete [] alignedTextureData;
2539 delete [] texture;
2541 if (FAILED(hr)) {
2542 return nullptr;
2543 }
2545 return tex;
2546 }
2548 void
2549 DrawTargetD2D::SetupEffectForRadialGradient(const RadialGradientPattern *aPattern)
2550 {
2551 mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->GetPassByIndex(0)->Apply(0);
2552 mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
2553 SetFloatVector(ShaderConstantRectD3D10(0, 0, 1.0f, 1.0f));
2555 float dimensions[] = { float(mSize.width), float(mSize.height), 0, 0 };
2556 mPrivateData->mEffect->GetVariableByName("dimensions")->AsVector()->
2557 SetFloatVector(dimensions);
2559 const GradientStopsD2D *stops =
2560 static_cast<const GradientStopsD2D*>(aPattern->mStops.get());
2562 RefPtr<ID3D10Texture2D> tex = CreateGradientTexture(stops);
2564 RefPtr<ID3D10ShaderResourceView> srView;
2565 mDevice->CreateShaderResourceView(tex, nullptr, byRef(srView));
2567 mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
2569 Point dc = aPattern->mCenter2 - aPattern->mCenter1;
2570 float dr = aPattern->mRadius2 - aPattern->mRadius1;
2572 float diffv[] = { dc.x, dc.y, dr, 0 };
2573 mPrivateData->mEffect->GetVariableByName("diff")->AsVector()->
2574 SetFloatVector(diffv);
2576 float center1[] = { aPattern->mCenter1.x, aPattern->mCenter1.y, dr, 0 };
2577 mPrivateData->mEffect->GetVariableByName("center1")->AsVector()->
2578 SetFloatVector(center1);
2580 mPrivateData->mEffect->GetVariableByName("radius1")->AsScalar()->
2581 SetFloat(aPattern->mRadius1);
2582 mPrivateData->mEffect->GetVariableByName("sq_radius1")->AsScalar()->
2583 SetFloat(pow(aPattern->mRadius1, 2));
2585 Matrix invTransform = mTransform;
2587 if (!invTransform.Invert()) {
2588 // Bail if the matrix is singular.
2589 return;
2590 }
2591 float matrix[] = { invTransform._11, invTransform._12, 0, 0,
2592 invTransform._21, invTransform._22, 0, 0,
2593 invTransform._31, invTransform._32, 1.0f, 0,
2594 0, 0, 0, 1.0f };
2596 mPrivateData->mEffect->GetVariableByName("DeviceSpaceToUserSpace")->
2597 AsMatrix()->SetMatrix(matrix);
2599 float A = dc.x * dc.x + dc.y * dc.y - dr * dr;
2601 uint32_t offset = 0;
2602 switch (stops->mStopCollection->GetExtendMode()) {
2603 case D2D1_EXTEND_MODE_WRAP:
2604 offset = 1;
2605 break;
2606 case D2D1_EXTEND_MODE_MIRROR:
2607 offset = 2;
2608 break;
2609 default:
2610 gfxWarning() << "This shouldn't happen! Invalid extend mode for gradient stops.";
2611 }
2613 if (A == 0) {
2614 mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->
2615 GetPassByIndex(offset * 2 + 1)->Apply(0);
2616 } else {
2617 mPrivateData->mEffect->GetVariableByName("A")->AsScalar()->SetFloat(A);
2618 mPrivateData->mEffect->GetTechniqueByName("SampleRadialGradient")->
2619 GetPassByIndex(offset * 2)->Apply(0);
2620 }
2621 }
2623 void
2624 DrawTargetD2D::SetupStateForRendering()
2625 {
2626 UINT stride = sizeof(Vertex);
2627 UINT offset = 0;
2628 ID3D10Buffer *buff = mPrivateData->mVB;
2630 mDevice->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
2631 mDevice->IASetVertexBuffers(0, 1, &buff, &stride, &offset);
2632 mDevice->IASetInputLayout(mPrivateData->mInputLayout);
2634 D3D10_VIEWPORT viewport;
2635 viewport.MaxDepth = 1;
2636 viewport.MinDepth = 0;
2637 viewport.Height = mSize.height;
2638 viewport.Width = mSize.width;
2639 viewport.TopLeftX = 0;
2640 viewport.TopLeftY = 0;
2642 mDevice->RSSetViewports(1, &viewport);
2643 }
2645 ID2D1Factory*
2646 DrawTargetD2D::factory()
2647 {
2648 if (mFactory) {
2649 return mFactory;
2650 }
2652 D2D1CreateFactoryFunc createD2DFactory;
2653 HMODULE d2dModule = LoadLibraryW(L"d2d1.dll");
2654 createD2DFactory = (D2D1CreateFactoryFunc)
2655 GetProcAddress(d2dModule, "D2D1CreateFactory");
2657 if (!createD2DFactory) {
2658 gfxWarning() << "Failed to locate D2D1CreateFactory function.";
2659 return nullptr;
2660 }
2662 D2D1_FACTORY_OPTIONS options;
2663 #ifdef _DEBUG
2664 options.debugLevel = D2D1_DEBUG_LEVEL_WARNING;
2665 #else
2666 options.debugLevel = D2D1_DEBUG_LEVEL_NONE;
2667 #endif
2669 HRESULT hr = createD2DFactory(D2D1_FACTORY_TYPE_MULTI_THREADED,
2670 __uuidof(ID2D1Factory),
2671 &options,
2672 (void**)&mFactory);
2674 if (FAILED(hr)) {
2675 gfxWarning() << "Failed to create Direct2D factory.";
2676 }
2678 return mFactory;
2679 }
2681 void
2682 DrawTargetD2D::CleanupD2D()
2683 {
2684 if (mFactory) {
2685 mFactory->Release();
2686 mFactory = nullptr;
2687 }
2688 }
2690 IDWriteFactory*
2691 DrawTargetD2D::GetDWriteFactory()
2692 {
2693 if (mDWriteFactory) {
2694 return mDWriteFactory;
2695 }
2697 decltype(DWriteCreateFactory)* createDWriteFactory;
2698 HMODULE dwriteModule = LoadLibraryW(L"dwrite.dll");
2699 createDWriteFactory = (decltype(DWriteCreateFactory)*)
2700 GetProcAddress(dwriteModule, "DWriteCreateFactory");
2702 if (!createDWriteFactory) {
2703 gfxWarning() << "Failed to locate DWriteCreateFactory function.";
2704 return nullptr;
2705 }
2707 HRESULT hr = createDWriteFactory(DWRITE_FACTORY_TYPE_SHARED, __uuidof(IDWriteFactory),
2708 reinterpret_cast<IUnknown**>(&mDWriteFactory));
2710 if (FAILED(hr)) {
2711 gfxWarning() << "Failed to create DWrite Factory.";
2712 }
2714 return mDWriteFactory;
2715 }
2717 void
2718 DrawTargetD2D::SetScissorToRect(IntRect *aRect)
2719 {
2720 D3D10_RECT rect;
2721 if (aRect) {
2722 rect.left = aRect->x;
2723 rect.right = aRect->XMost();
2724 rect.top = aRect->y;
2725 rect.bottom = aRect->YMost();
2726 } else {
2727 rect.left = rect.top = INT32_MIN;
2728 rect.right = rect.bottom = INT32_MAX;
2729 }
2731 mDevice->RSSetScissorRects(1, &rect);
2732 }
2734 void
2735 DrawTargetD2D::PushD2DLayer(ID2D1RenderTarget *aRT, ID2D1Geometry *aGeometry, ID2D1Layer *aLayer, const D2D1_MATRIX_3X2_F &aTransform)
2736 {
2737 D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE;
2738 D2D1_LAYER_OPTIONS1 options1 = D2D1_LAYER_OPTIONS1_NONE;
2740 if (aRT->GetPixelFormat().alphaMode == D2D1_ALPHA_MODE_IGNORE) {
2741 options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
2742 options1 = D2D1_LAYER_OPTIONS1_IGNORE_ALPHA | D2D1_LAYER_OPTIONS1_INITIALIZE_FROM_BACKGROUND;
2743 }
2745 RefPtr<ID2D1DeviceContext> dc;
2746 HRESULT hr = aRT->QueryInterface(IID_ID2D1DeviceContext, (void**)((ID2D1DeviceContext**)byRef(dc)));
2748 if (FAILED(hr)) {
2749 aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), aGeometry,
2750 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
2751 1.0, nullptr, options),
2752 aLayer);
2753 } else {
2754 dc->PushLayer(D2D1::LayerParameters1(D2D1::InfiniteRect(), aGeometry,
2755 D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, aTransform,
2756 1.0, nullptr, options1),
2757 aLayer);
2758 }
2759 }
2761 }
2762 }