Bug 772726: Factor out pixel-aligned clip handling in DrawTargetD2D. r=jrmuizel
authorBas Schouten <bschouten@mozilla.com>
Tue, 18 Sep 2012 23:04:42 +0000
changeset 107550 c0a341f7b0c9231f14ccc7a6161684c7b560bd52
parent 107549 be731f9147dc0c6c5c3c9df1c8a8ee5c6d5cc69d
child 107551 def643b50ef1880bb4e7bc0dd03878da0fd8be8b
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewersjrmuizel
bugs772726
milestone18.0a1
Bug 772726: Factor out pixel-aligned clip handling in DrawTargetD2D. r=jrmuizel
gfx/2d/DrawTargetD2D.cpp
gfx/2d/DrawTargetD2D.h
gfx/2d/ShadersD2D.fx
gfx/2d/ShadersD2D.h
--- a/gfx/2d/DrawTargetD2D.cpp
+++ b/gfx/2d/DrawTargetD2D.cpp
@@ -95,17 +95,29 @@ public:
 
     hr = mDT->mRT->CreateSharedBitmap(IID_IDXGISurface, surf,
                                       &props, byRef(mOldSurfBitmap));
 
     if (FAILED(hr)) {
       gfxWarning() << "Failed to create shared bitmap for old surface.";
     }
 
-    mClippedArea = mDT->GetClippedGeometry();
+    IntRect clipBounds;
+    mClippedArea = mDT->GetClippedGeometry(&clipBounds);
+
+    if (!clipBounds.IsEqualEdges(IntRect(IntPoint(0, 0), mDT->mSize))) {
+      // We still need to take into account clipBounds if it contains additional
+      // clipping information.
+      RefPtr<ID2D1RectangleGeometry> rectGeom;
+      factory()->CreateRectangleGeometry(D2D1::Rect(clipBounds.x, clipBounds.y,
+                                                    clipBounds.XMost(), clipBounds.YMost()),
+                                         byRef(rectGeom));
+
+      mClippedArea = mDT->Intersect(mClippedArea, rectGeom);
+    }
   }
 
   ID2D1Factory *factory() { return mDT->factory(); }
 
   ~AutoSaveRestoreClippedOut()
   {
     if (!mOldSurfBitmap) {
       return;
@@ -320,16 +332,18 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
                                      Float aSigma,
                                      CompositionOp aOperator)
 {
   RefPtr<ID3D10ShaderResourceView> srView = nullptr;
   if (aSurface->GetType() != SURFACE_D2D1_DRAWTARGET) {
     return;
   }
 
+  SetScissorToRect(nullptr);
+
   // XXX - This function is way too long, it should be split up soon to make
   // it more graspable!
 
   Flush();
 
   AutoSaveRestoreClippedOut restoreClippedOut(this);
 
   if (!IsOperatorBoundByMask(aOperator)) {
@@ -352,18 +366,19 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
 
 
   RefPtr<ID3D10RenderTargetView> destRTView = mRTView;
   RefPtr<ID3D10Texture2D> destTexture;
   HRESULT hr;
 
   RefPtr<ID3D10Texture2D> maskTexture;
   RefPtr<ID3D10ShaderResourceView> maskSRView;
+  IntRect clipBounds;
   if (mPushedClips.size()) {
-    EnsureClipMaskTexture();
+    EnsureClipMaskTexture(&clipBounds);
 
     mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(maskSRView));
   }
 
   IntSize srcSurfSize;
   ID3D10RenderTargetView *rtViews;
   D3D10_VIEWPORT viewport;
 
@@ -609,16 +624,17 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
   if (mPushedClips.size()) {
     mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(maskSRView);
     mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
       SetFloatVector(ShaderConstantRectD3D10(shadowDest.x / mSize.width, shadowDest.y / mSize.height,
                                              Float(aSurface->GetSize().width) / mSize.width,
                                              Float(aSurface->GetSize().height) / mSize.height));
     mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
       GetPassByIndex(2)->Apply(0);
+    SetScissorToRect(&clipBounds);
   } else {
     mPrivateData->mEffect->GetTechniqueByName("SampleTextureWithShadow")->
       GetPassByIndex(1)->Apply(0);
   }
 
   mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
 
   mDevice->Draw(4, 0);
@@ -634,16 +650,17 @@ DrawTargetD2D::DrawSurfaceWithShadow(Sou
 
   if (mPushedClips.size()) {
     mPrivateData->mEffect->GetVariableByName("MaskTexCoords")->AsVector()->
       SetFloatVector(ShaderConstantRectD3D10(aDest.x / mSize.width, aDest.y / mSize.height,
                                              Float(aSurface->GetSize().width) / mSize.width,
                                              Float(aSurface->GetSize().height) / mSize.height));
     mPrivateData->mEffect->GetTechniqueByName("SampleMaskedTexture")->
       GetPassByIndex(0)->Apply(0);
+    // We've set the scissor rect here for the previous draw call.
   } else {
     mPrivateData->mEffect->GetTechniqueByName("SampleTexture")->
       GetPassByIndex(0)->Apply(0);
   }
 
   mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
 
   mDevice->Draw(4, 0);
@@ -1001,25 +1018,30 @@ DrawTargetD2D::PushClipRect(const Rect &
     pathBuilder->LineTo(aRect.BottomRight());
     pathBuilder->LineTo(aRect.BottomLeft());
     pathBuilder->Close();
     RefPtr<Path> path = pathBuilder->Finish();
     return PushClip(path);
   }
 
   PushedClip clip;
+  Rect rect = mTransform.TransformBounds(aRect);
+  IntRect intRect;
+  clip.mIsPixelAligned = rect.ToIntRect(&intRect);
+
   // Do not store the transform, just store the device space rectangle directly.
-  clip.mBounds = D2DRect(mTransform.TransformBounds(aRect));
+  clip.mBounds = D2DRect(rect);
 
   mPushedClips.push_back(clip);
 
   mRT->SetTransform(D2D1::IdentityMatrix());
   mTransformDirty = true;
+
   if (mClipsArePushed) {
-    mRT->PushAxisAlignedClip(clip.mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+    mRT->PushAxisAlignedClip(clip.mBounds, clip.mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
   }
 }
 
 void
 DrawTargetD2D::PopClip()
 {
   mCurrentClipMaskTexture = nullptr;
   mCurrentClippedGeometry = nullptr;
@@ -1570,69 +1592,116 @@ DrawTargetD2D::FinalizeRTForOperation(Co
 
     mPrivateData->mEffect->GetVariableByName("mask")->AsShaderResource()->SetResource(mSRView);
 
     SetupEffectForRadialGradient(pat);
   }
 
   mDevice->OMSetBlendState(GetBlendStateForOperator(aOperator), nullptr, 0xffffffff);
   
+  SetScissorToRect(nullptr);
   mDevice->Draw(4, 0);
 }
 
 TemporaryRef<ID2D1Geometry>
 DrawTargetD2D::ConvertRectToGeometry(const D2D1_RECT_F& aRect)
 {
   RefPtr<ID2D1RectangleGeometry> rectGeom;
   factory()->CreateRectangleGeometry(&aRect, byRef(rectGeom));
   return rectGeom.forget();
 }
 
+TemporaryRef<ID2D1Geometry>
+DrawTargetD2D::GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform)
+{
+  RefPtr<ID2D1PathGeometry> tmpGeometry;
+  factory()->CreatePathGeometry(byRef(tmpGeometry));
+  RefPtr<ID2D1GeometrySink> currentSink;
+  tmpGeometry->Open(byRef(currentSink));
+  aGeometry->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
+                      aTransform, currentSink);
+  currentSink->Close();
+  return tmpGeometry;
+}
+
+TemporaryRef<ID2D1Geometry>
+DrawTargetD2D::Intersect(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB)
+{
+  RefPtr<ID2D1PathGeometry> pathGeom;
+  factory()->CreatePathGeometry(byRef(pathGeom));
+  RefPtr<ID2D1GeometrySink> sink;
+  pathGeom->Open(byRef(sink));
+  aGeometryA->CombineWithGeometry(aGeometryB, D2D1_COMBINE_MODE_INTERSECT, nullptr, sink);
+  sink->Close();
+
+  return pathGeom;
+}
+
 static D2D1_RECT_F
 IntersectRect(const D2D1_RECT_F& aRect1, const D2D1_RECT_F& aRect2)
 {
   D2D1_RECT_F result;
   result.left = max(aRect1.left, aRect2.left);
   result.top = max(aRect1.top, aRect2.top);
   result.right = min(aRect1.right, aRect2.right);
   result.bottom = min(aRect1.bottom, aRect2.bottom);
   return result;
 }
 
 TemporaryRef<ID2D1Geometry>
-DrawTargetD2D::GetClippedGeometry()
+DrawTargetD2D::GetClippedGeometry(IntRect *aClipBounds)
 {
   if (mCurrentClippedGeometry) {
+    *aClipBounds = mCurrentClipBounds;
     return mCurrentClippedGeometry;
   }
 
+  mCurrentClipBounds = IntRect(IntPoint(0, 0), mSize);
+
   // if pathGeom is null then pathRect represents the path.
   RefPtr<ID2D1Geometry> pathGeom;
   D2D1_RECT_F pathRect;
+  bool pathRectIsAxisAligned = false;
   std::vector<DrawTargetD2D::PushedClip>::iterator iter = mPushedClips.begin();
+  
   if (iter->mPath) {
-    RefPtr<ID2D1PathGeometry> tmpGeometry;
-    factory()->CreatePathGeometry(byRef(tmpGeometry));
-    RefPtr<ID2D1GeometrySink> currentSink;
-    tmpGeometry->Open(byRef(currentSink));
-    iter->mPath->GetGeometry()->Simplify(D2D1_GEOMETRY_SIMPLIFICATION_OPTION_CUBICS_AND_LINES,
-                                         iter->mTransform, currentSink);
-    currentSink->Close();
-    pathGeom = tmpGeometry.forget();
+    pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
   } else {
     pathRect = iter->mBounds;
+    pathRectIsAxisAligned = iter->mIsPixelAligned;
   }
 
   iter++;
   for (;iter != mPushedClips.end(); iter++) {
+    // Do nothing but add it to the current clip bounds.
+    if (!iter->mPath && iter->mIsPixelAligned) {
+      mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+        IntRect(iter->mBounds.left, iter->mBounds.top,
+                int32_t(iter->mBounds.right - iter->mBounds.left),
+                int32_t(iter->mBounds.bottom - iter->mBounds.top)));
+      continue;
+    }
+
     if (!pathGeom) {
+      if (pathRectIsAxisAligned) {
+        mCurrentClipBounds.IntersectRect(mCurrentClipBounds,
+          IntRect(pathRect.left, pathRect.top,
+                  int32_t(pathRect.right - pathRect.left),
+                  int32_t(pathRect.bottom - pathRect.top)));
+      }
       if (iter->mPath) {
-        pathGeom = ConvertRectToGeometry(pathRect);
+        // See if pathRect needs to go into the path geometry.
+        if (!pathRectIsAxisAligned) {
+          pathGeom = ConvertRectToGeometry(pathRect);
+        } else {
+          pathGeom = GetTransformedGeometry(iter->mPath->GetGeometry(), iter->mTransform);
+        }
       } else {
         pathRect = IntersectRect(pathRect, iter->mBounds);
+        pathRectIsAxisAligned = false;
         continue;
       }
     }
 
     RefPtr<ID2D1PathGeometry> newGeom;
     factory()->CreatePathGeometry(byRef(newGeom));
 
     RefPtr<ID2D1GeometrySink> currentSink;
@@ -1647,20 +1716,25 @@ DrawTargetD2D::GetClippedGeometry()
                                     D2D1::IdentityMatrix(), currentSink);
     }
 
     currentSink->Close();
 
     pathGeom = newGeom.forget();
   }
 
+  // For now we need mCurrentClippedGeometry to always be non-NULL. This method
+  // might seem a little strange but it is just fine, if pathGeom is NULL
+  // pathRect will always still contain 1 clip unaccounted for regardless of
+  // mCurrentClipBounds.
   if (!pathGeom) {
     pathGeom = ConvertRectToGeometry(pathRect);
   }
   mCurrentClippedGeometry = pathGeom.forget();
+  *aClipBounds = mCurrentClipBounds;
   return mCurrentClippedGeometry;
 }
 
 TemporaryRef<ID2D1RenderTarget>
 DrawTargetD2D::CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat)
 {
   HRESULT hr;
 
@@ -1754,17 +1828,17 @@ DrawTargetD2D::PushClipsToRT(ID2D1Render
         options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE;
       }
 
       aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry,
                                             D2D1_ANTIALIAS_MODE_PER_PRIMITIVE,
                                             iter->mTransform, 1.0f, nullptr,
                                             options), iter->mLayer);
     } else {
-      aRT->PushAxisAlignedClip(iter->mBounds, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
+      aRT->PushAxisAlignedClip(iter->mBounds, iter->mIsPixelAligned ? D2D1_ANTIALIAS_MODE_ALIASED : D2D1_ANTIALIAS_MODE_PER_PRIMITIVE);
     }
   }
 }
 
 void
 DrawTargetD2D::PopClipsFromRT(ID2D1RenderTarget *aRT)
 {
   for (int i = mPushedClips.size() - 1; i >= 0; i--) {
@@ -1772,22 +1846,25 @@ DrawTargetD2D::PopClipsFromRT(ID2D1Rende
       aRT->PopLayer();
     } else {
       aRT->PopAxisAlignedClip();
     }
   }
 }
 
 void
-DrawTargetD2D::EnsureClipMaskTexture()
+DrawTargetD2D::EnsureClipMaskTexture(IntRect *aBounds)
 {
   if (mCurrentClipMaskTexture || mPushedClips.empty()) {
+    *aBounds = mCurrentClipBounds;
     return;
   }
   
+  RefPtr<ID2D1Geometry> geometry = GetClippedGeometry(aBounds);
+
   CD3D10_TEXTURE2D_DESC desc(DXGI_FORMAT_A8_UNORM,
                              mSize.width,
                              mSize.height,
                              1, 1);
   desc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
 
   HRESULT hr = mDevice->CreateTexture2D(&desc, nullptr, byRef(mCurrentClipMaskTexture));
 
@@ -1801,18 +1878,16 @@ DrawTargetD2D::EnsureClipMaskTexture()
   if (!rt) {
     gfxWarning() << "Failed to create RT for ClipMask!";
     return;
   }
   
   RefPtr<ID2D1SolidColorBrush> brush;
   rt->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), byRef(brush));
     
-  RefPtr<ID2D1Geometry> geometry = GetClippedGeometry();
-
   rt->BeginDraw();
   rt->Clear(D2D1::ColorF(0, 0));
   rt->FillGeometry(geometry, brush);
   rt->EndDraw();
 }
 
 bool
 DrawTargetD2D::FillGlyphsManual(ScaledFontDWrite *aFont,
@@ -1935,34 +2010,39 @@ DrawTargetD2D::FillGlyphsManual(ScaledFo
   FLOAT color[4] = { aColor.r, aColor.g, aColor.b, aColor.a };
   mPrivateData->mEffect->GetVariableByName("TextColor")->AsVector()->
     SetFloatVector(color);
   
   mPrivateData->mEffect->GetVariableByName("tex")->AsShaderResource()->SetResource(srView);
 
   bool isMasking = false;
 
+  IntRect clipBoundsStorage;
+  IntRect *clipBounds = nullptr;
+
   if (!mPushedClips.empty()) {
-    RefPtr<ID2D1Geometry> geom = GetClippedGeometry();
+    clipBounds = &clipBoundsStorage;
+    RefPtr<ID2D1Geometry> geom = GetClippedGeometry(clipBounds);
 
     RefPtr<ID2D1RectangleGeometry> rectGeom;
     factory()->CreateRectangleGeometry(D2D1::RectF(rectBounds.x, rectBounds.y,
                                                    rectBounds.width + rectBounds.x,
                                                    rectBounds.height + rectBounds.y),
                                        byRef(rectGeom));
 
     D2D1_GEOMETRY_RELATION relation;
     if (FAILED(geom->CompareWithGeometry(rectGeom, D2D1::IdentityMatrix(), &relation)) ||
-        relation != D2D1_GEOMETRY_RELATION_CONTAINS) {
+        relation != D2D1_GEOMETRY_RELATION_CONTAINS ) {
       isMasking = true;
     }        
   }
   
   if (isMasking) {
-    EnsureClipMaskTexture();
+    clipBounds = &clipBoundsStorage;
+    EnsureClipMaskTexture(clipBounds);
 
     RefPtr<ID3D10ShaderResourceView> srViewMask;
     hr = mDevice->CreateShaderResourceView(mCurrentClipMaskTexture, nullptr, byRef(srViewMask));
 
     if (FAILED(hr)) {
       return false;
     }
 
@@ -1978,17 +2058,17 @@ DrawTargetD2D::FillGlyphsManual(ScaledFo
   }  
 
   RefPtr<ID3D10RenderTargetView> rtView;
   ID3D10RenderTargetView *rtViews;
   mDevice->CreateRenderTargetView(mTexture, nullptr, byRef(rtView));
 
   rtViews = rtView;
   mDevice->OMSetRenderTargets(1, &rtViews, nullptr);
-
+  SetScissorToRect(clipBounds);
   mDevice->Draw(4, 0);
   return true;
 }
 
 TemporaryRef<ID2D1Brush>
 DrawTargetD2D::CreateBrushForPattern(const Pattern &aPattern, Float aAlpha)
 {
   if (IsPatternSupportedByD2D(aPattern)) {
@@ -2608,10 +2688,27 @@ DrawTargetD2D::GetDWriteFactory()
 
   if (FAILED(hr)) {
     gfxWarning() << "Failed to create DWrite Factory.";
   }
 
   return mDWriteFactory;
 }
 
+void
+DrawTargetD2D::SetScissorToRect(IntRect *aRect)
+{
+  D3D10_RECT rect;
+  if (aRect) {
+    rect.left = aRect->x;
+    rect.right = aRect->XMost();
+    rect.top = aRect->y;
+    rect.bottom = aRect->YMost();
+  } else {
+    rect.left = rect.left = INT32_MIN;
+    rect.right = rect.top = INT32_MAX;
+  }
+
+  mDevice->RSSetScissorRects(1, &rect);
+}
+
 }
 }
--- a/gfx/2d/DrawTargetD2D.h
+++ b/gfx/2d/DrawTargetD2D.h
@@ -167,51 +167,67 @@ private:
   ID3D10BlendState *GetBlendStateForOperator(CompositionOp aOperator);
   ID2D1RenderTarget *GetRTForOperation(CompositionOp aOperator, const Pattern &aPattern);
   void FinalizeRTForOperation(CompositionOp aOperator, const Pattern &aPattern, const Rect &aBounds);  void EnsureViews();
   void PopAllClips();
   void PushClipsToRT(ID2D1RenderTarget *aRT);
   void PopClipsFromRT(ID2D1RenderTarget *aRT);
 
   // This function ensures mCurrentClipMaskTexture contains a texture containing
-  // a mask corresponding with the current DrawTarget clip.
-  void EnsureClipMaskTexture();
+  // a mask corresponding with the current DrawTarget clip. See
+  // GetClippedGeometry for a description of aClipBounds.
+  void EnsureClipMaskTexture(IntRect *aClipBounds);
 
   bool FillGlyphsManual(ScaledFontDWrite *aFont,
                         const GlyphBuffer &aBuffer,
                         const Color &aColor,
                         IDWriteRenderingParams *aParams,
                         const DrawOptions &aOptions = DrawOptions());
 
   TemporaryRef<ID2D1RenderTarget> CreateRTForTexture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   TemporaryRef<ID2D1Geometry> ConvertRectToGeometry(const D2D1_RECT_F& aRect);
-  TemporaryRef<ID2D1Geometry> GetClippedGeometry();
+  TemporaryRef<ID2D1Geometry> GetTransformedGeometry(ID2D1Geometry *aGeometry, const D2D1_MATRIX_3X2_F &aTransform);
+  TemporaryRef<ID2D1Geometry> Intersect(ID2D1Geometry *aGeometryA, ID2D1Geometry *aGeometryB);
+
+  // This returns the clipped geometry, in addition it returns aClipBounds which
+  // represents the intersection of all pixel-aligned rectangular clips that
+  // are currently set. The returned clipped geometry must be clipped by these
+  // bounds to correctly reflect the total clip. This is in device space.
+  TemporaryRef<ID2D1Geometry> GetClippedGeometry(IntRect *aClipBounds);
 
   TemporaryRef<ID2D1Brush> CreateBrushForPattern(const Pattern &aPattern, Float aAlpha = 1.0f);
 
   TemporaryRef<ID3D10Texture2D> CreateGradientTexture(const GradientStopsD2D *aStops);
   TemporaryRef<ID3D10Texture2D> CreateTextureForAnalysis(IDWriteGlyphRunAnalysis *aAnalysis, const IntRect &aBounds);
 
   // This creates a (partially) uploaded bitmap for a DataSourceSurface. It
   // uploads the minimum requirement and possibly downscales. It adjusts the
   // input Matrix to compensate.
   TemporaryRef<ID2D1Bitmap> CreatePartialBitmapForSurface(DataSourceSurface *aSurface, Matrix &aMatrix,
                                                           ExtendMode aExtendMode);
 
   void SetupEffectForRadialGradient(const RadialGradientPattern *aPattern);
   void SetupStateForRendering();
 
+  // Set the scissor rect to a certain IntRects, resets the scissor rect to
+  // surface bounds when NULL is specified.
+  void SetScissorToRect(IntRect *aRect);
+
   static const uint32_t test = 4;
 
   IntSize mSize;
 
   RefPtr<ID3D10Device1> mDevice;
   RefPtr<ID3D10Texture2D> mTexture;
   RefPtr<ID3D10Texture2D> mCurrentClipMaskTexture;
   RefPtr<ID2D1Geometry> mCurrentClippedGeometry;
+  // This is only valid if mCurrentClippedGeometry is non-null. And will
+  // only be the intersection of all pixel-aligned retangular clips. This is in
+  // device space.
+  IntRect mCurrentClipBounds;
   mutable RefPtr<ID2D1RenderTarget> mRT;
 
   // We store this to prevent excessive SetTextRenderingParams calls.
   RefPtr<IDWriteRenderingParams> mTextRenderingParams;
 
   // Temporary texture and render target used for supporting alternative operators.
   RefPtr<ID3D10Texture2D> mTempTexture;
   RefPtr<ID3D10RenderTargetView> mRTView;
@@ -219,17 +235,22 @@ private:
   RefPtr<ID2D1RenderTarget> mTempRT;
   RefPtr<ID3D10RenderTargetView> mTempRTView;
 
   // List of pushed clips.
   struct PushedClip
   {
     RefPtr<ID2D1Layer> mLayer;
     D2D1_RECT_F mBounds;
-    D2D1_MATRIX_3X2_F mTransform;
+    union {
+      // If mPath is non-NULL, the mTransform member will be used, otherwise
+      // the mIsPixelAligned member is valid.
+      D2D1_MATRIX_3X2_F mTransform;
+      bool mIsPixelAligned;
+    };
     RefPtr<PathD2D> mPath;
   };
   std::vector<PushedClip> mPushedClips;
 
   // We cache ID2D1Layer objects as it causes D2D to keep around textures that
   // serve as the temporary surfaces for these operations. As texture creation
   // is quite expensive this considerably improved performance.
   // Careful here, RAII will not ensure destruction of the RefPtrs.
--- a/gfx/2d/ShadersD2D.fx
+++ b/gfx/2d/ShadersD2D.fx
@@ -93,17 +93,17 @@ sampler sShadowSampler = sampler_state {
     Texture = tex;
     AddressU = Border;
     AddressV = Border;
     BorderColor = float4(0, 0, 0, 0);
 };
 
 RasterizerState TextureRast
 {
-  ScissorEnable = False;
+  ScissorEnable = True;
   CullMode = None;
 };
 
 BlendState ShadowBlendH
 {
   BlendEnable[0] = False;
   RenderTargetWriteMask[0] = 0xF;
 };
--- a/gfx/2d/ShadersD2D.h
+++ b/gfx/2d/ShadersD2D.h
@@ -70,17 +70,17 @@ SamplerState sShadowSampler
     Filter   = uint(MIN_MAG_MIP_LINEAR /* 21 */);
     Texture  = tex;
     AddressU = uint(BORDER /* 4 */);
     AddressV = uint(BORDER /* 4 */);
     BorderColor = float4(0, 0, 0, 0);
 };
 RasterizerState TextureRast
 {
-    ScissorEnable = bool(FALSE /* 0 */);
+    ScissorEnable = bool(TRUE /* 1 */);
     CullMode = uint(NONE /* 1 */);
 };
 BlendState ShadowBlendH
 {
     BlendEnable[0] = bool(FALSE /* 0 */);
     RenderTargetWriteMask[0] = byte(0x0f);
 };
 BlendState ShadowBlendV
@@ -3052,20 +3052,20 @@ technique10 SampleTextTexture
     }
 
 }
 
 #endif
 
 const BYTE d2deffect[] =
 {
-     68,  88,  66,  67, 180,  92, 
-     99,   8,  31, 243, 141,  10, 
-    239,  43,  71, 236,  45, 138, 
-     71, 112,   1,   0,   0,   0, 
+     68,  88,  66,  67, 162, 185, 
+     42, 249, 229,   2,  50,  89, 
+      3, 159,  21, 149,  11, 251, 
+     62, 135,   1,   0,   0,   0, 
     144, 191,   0,   0,   1,   0, 
       0,   0,  36,   0,   0,   0, 
      70,  88,  49,  48, 100, 191, 
       0,   0,   1,  16, 255, 254, 
       3,   0,   0,   0,  15,   0, 
       0,   0,  11,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
@@ -3207,17 +3207,17 @@ const BYTE d2deffect[] =
      97, 116, 101,   0, 238,   2, 
       0,   0,   2,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   4,   0, 
       0,   0,  84, 101, 120, 116, 
     117, 114, 101,  82,  97, 115, 
     116,   0,   1,   0,   0,   0, 
-      2,   0,   0,   0,   0,   0, 
+      2,   0,   0,   0,   1,   0, 
       0,   0,   1,   0,   0,   0, 
       2,   0,   0,   0,   1,   0, 
       0,   0,  66, 108, 101, 110, 
     100,  83, 116,  97, 116, 101, 
       0,  62,   3,   0,   0,   2, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0, 
       0,   0,   0,   0,   0,   0,