Bug 586683 - Part 3 - Add resolution handling to ThebesLayerD3D9. r=Bas a=blocking2.0
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 09 Feb 2011 09:39:09 +1300
changeset 62164 9feb4c4ae2e7d5fa195b12cfb538e8a07a017149
parent 62163 acec86b21f5ee4b38145d92cc182a206be0e3299
child 62165 15396f4b2b54a0987093169cb14fe9aa2ec1b0e5
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersBas, blocking2.0
bugs586683
milestone2.0b12pre
Bug 586683 - Part 3 - Add resolution handling to ThebesLayerD3D9. r=Bas a=blocking2.0
gfx/layers/d3d9/ThebesLayerD3D9.cpp
gfx/layers/d3d9/ThebesLayerD3D9.h
--- a/gfx/layers/d3d9/ThebesLayerD3D9.cpp
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.cpp
@@ -71,40 +71,42 @@ void
 ThebesLayerD3D9::InvalidateRegion(const nsIntRegion &aRegion)
 {
   mValidRegion.Sub(mValidRegion, aRegion);
 }
 
 void
 ThebesLayerD3D9::CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset,
                             IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset,
-                            const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion)
+                            const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion,
+                            float aXRes, float aYRes)
 {
   nsRefPtr<IDirect3DSurface9> srcSurface, dstSurface;
   aSrc->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
   aDest->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
 
   nsIntRegion retainedRegion;
   nsIntRegionRectIterator iter(aCopyRegion);
   const nsIntRect *r;
   while ((r = iter.Next())) {
     if (r->width * r->height > RETENTION_THRESHOLD) {
       RECT oldRect, newRect;
 
       // Calculate the retained rectangle's position on the old and the new
-      // surface.
-      oldRect.left = r->x - aSrcOffset.x;
-      oldRect.top = r->y - aSrcOffset.y;
-      oldRect.right = oldRect.left + r->width;
-      oldRect.bottom = oldRect.top + r->height;
+      // surface. We need to scale these rectangles since the visible
+      // region is in unscaled units, and the texture size has been scaled.
+      oldRect.left = UINT(floor((r->x - aSrcOffset.x) * aXRes));
+      oldRect.top = UINT(floor((r->y - aSrcOffset.y) * aYRes));
+      oldRect.right = oldRect.left + UINT(ceil(r->width * aXRes));
+      oldRect.bottom = oldRect.top + UINT(ceil(r->height * aYRes));
 
-      newRect.left = r->x - aDestOffset.x;
-      newRect.top = r->y - aDestOffset.y;
-      newRect.right = newRect.left + r->width;
-      newRect.bottom = newRect.top + r->height;
+      newRect.left = UINT(floor((r->x - aDestOffset.x) * aXRes));
+      newRect.top = UINT(floor((r->y - aDestOffset.y) * aYRes));
+      newRect.right = newRect.left + UINT(ceil(r->width * aXRes));
+      newRect.bottom = newRect.top + UINT(ceil(r->height * aYRes));
 
       // Copy data from our old texture to the new one
       HRESULT hr = device()->
         StretchRect(srcSurface, &oldRect, dstSurface, &newRect, D3DTEXF_NONE);
 
       if (SUCCEEDED(hr)) {
         retainedRegion.Or(retainedRegion, *r);
       }
@@ -120,16 +122,25 @@ static PRUint64 RectArea(const nsIntRect
   return aRect.width*PRUint64(aRect.height);
 }
 
 void
 ThebesLayerD3D9::UpdateTextures(SurfaceMode aMode)
 {
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
 
+  float xres, yres;
+  GetDesiredResolutions(xres, yres);
+
+  // If our resolution changed, we need new sized textures, delete the old ones.
+  if (ResolutionChanged(xres, yres)) {
+      mTexture = nsnull;
+      mTextureOnWhite = nsnull;
+  }
+
   if (HaveTextures(aMode)) {
     if (mTextureRect != visibleRect) {
       nsRefPtr<IDirect3DTexture9> oldTexture = mTexture;
       nsRefPtr<IDirect3DTexture9> oldTextureOnWhite = mTextureOnWhite;
 
       NS_ASSERTION(mTextureRect.Contains(mValidRegion.GetBounds()),
                    "How can we have valid data outside the texture?");
       nsIntRegion retainRegion;
@@ -142,20 +153,20 @@ ThebesLayerD3D9::UpdateTextures(SurfaceM
       // If our texture creation failed this can mean a device reset is pending and we
       // should silently ignore the failure. In the future when device failures
       // are properly handled we should test for the type of failure and gracefully
       // handle different failures. See bug 569081.
       if (!HaveTextures(aMode)) {
         mValidRegion.SetEmpty();
       } else {
         CopyRegion(oldTexture, mTextureRect.TopLeft(), mTexture, visibleRect.TopLeft(),
-                   retainRegion, &mValidRegion);
+                   retainRegion, &mValidRegion, xres, yres);
         if (aMode == SURFACE_COMPONENT_ALPHA) {
           CopyRegion(oldTextureOnWhite, mTextureRect.TopLeft(), mTextureOnWhite, visibleRect.TopLeft(),
-                     retainRegion, &mValidRegion);
+                     retainRegion, &mValidRegion, xres, yres);
         }
       }
 
       mTextureRect = visibleRect;
     }
   } else {
     CreateNewTextures(gfxIntSize(visibleRect.width, visibleRect.height), aMode);
     mTextureRect = visibleRect;
@@ -321,34 +332,37 @@ ThebesLayerD3D9::VerifyContentType(Surfa
   mValidRegion.SetEmpty();
 }
 
 class OpaqueRenderer {
 public:
   OpaqueRenderer(const nsIntRegion& aUpdateRegion) :
     mUpdateRegion(aUpdateRegion), mDC(NULL) {}
   ~OpaqueRenderer() { End(); }
-  already_AddRefed<gfxWindowsSurface> Begin(LayerD3D9* aLayer);
+  already_AddRefed<gfxWindowsSurface> Begin(LayerD3D9* aLayer, float aXRes, float aYRes);
   void End();
   IDirect3DTexture9* GetTexture() { return mTmpTexture; }
 
 private:
   const nsIntRegion& mUpdateRegion;
   nsRefPtr<IDirect3DTexture9> mTmpTexture;
   nsRefPtr<IDirect3DSurface9> mSurface;
   HDC mDC;
 };
 
 already_AddRefed<gfxWindowsSurface>
-OpaqueRenderer::Begin(LayerD3D9* aLayer)
+OpaqueRenderer::Begin(LayerD3D9* aLayer, float aXRes, float aYRes)
 {
   nsIntRect bounds = mUpdateRegion.GetBounds();
+  gfxIntSize scaledSize;
+  scaledSize.width = PRInt32(ceil(bounds.width * aXRes));
+  scaledSize.height = PRInt32(ceil(bounds.height * aYRes));
 
   HRESULT hr = aLayer->device()->
-      CreateTexture(bounds.width, bounds.height, 1, 0, D3DFMT_X8R8G8B8,
+      CreateTexture(scaledSize.width, scaledSize.height, 1, 0, D3DFMT_X8R8G8B8,
                     D3DPOOL_SYSTEMMEM, getter_AddRefs(mTmpTexture), NULL);
 
   if (FAILED(hr)) {
     aLayer->ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr);
     return nsnull;
   }
 
   hr = mTmpTexture->GetSurfaceLevel(0, getter_AddRefs(mSurface));
@@ -376,45 +390,52 @@ OpaqueRenderer::End()
     mSurface->ReleaseDC(mDC);
     mSurface = NULL;
     mDC = NULL;
   }
 }
 
 static void
 FillSurface(gfxASurface* aSurface, const nsIntRegion& aRegion,
-            const nsIntPoint& aOffset, const gfxRGBA& aColor)
+            const nsIntPoint& aOffset, const gfxRGBA& aColor,
+            float aXRes, float aYRes)
 {
   nsRefPtr<gfxContext> ctx = new gfxContext(aSurface);
+  ctx->Scale(aXRes, aYRes);
   ctx->Translate(-gfxPoint(aOffset.x, aOffset.y));
   gfxUtils::ClipToRegion(ctx, aRegion);
   ctx->SetColor(aColor);
   ctx->Paint();
 }
 
 void
-ThebesLayerD3D9::DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode)
+ThebesLayerD3D9::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode)
 {
   HRESULT hr;
   nsIntRect visibleRect = mVisibleRegion.GetBounds();
+  float xres, yres;
+  GetDesiredResolutions(xres, yres);
 
   nsRefPtr<gfxASurface> destinationSurface;
   nsIntRect bounds = aRegion.GetBounds();
+  gfxIntSize scaledSize;
+  scaledSize.width = PRInt32(ceil(bounds.width * xres));
+  scaledSize.height = PRInt32(ceil(bounds.height * yres));
   nsRefPtr<IDirect3DTexture9> tmpTexture;
   OpaqueRenderer opaqueRenderer(aRegion);
   OpaqueRenderer opaqueRendererOnWhite(aRegion);
 
   switch (aMode)
   {
     case SURFACE_OPAQUE:
-      destinationSurface = opaqueRenderer.Begin(this);
+      destinationSurface = opaqueRenderer.Begin(this, xres, yres);
       break;
 
     case SURFACE_SINGLE_CHANNEL_ALPHA: {
-      hr = device()->CreateTexture(bounds.width, bounds.height, 1,
+      hr = device()->CreateTexture(scaledSize.width, scaledSize.height, 1,
                                    0, D3DFMT_A8R8G8B8,
                                    D3DPOOL_SYSTEMMEM, getter_AddRefs(tmpTexture), NULL);
 
       if (FAILED(hr)) {
         ReportFailure(NS_LITERAL_CSTRING("Failed to create temporary texture in system memory."), hr);
         return;
       }
 
@@ -427,37 +448,40 @@ ThebesLayerD3D9::DrawRegion(const nsIntR
       // end of rendering, it's safe to enable Cleartype since all the Cleartype
       // glyphs must be over (or under) opaque pixels.
       dest->SetSubpixelAntialiasingEnabled(!(mContentFlags & CONTENT_COMPONENT_ALPHA));
       destinationSurface = dest.forget();
       break;
     }
 
     case SURFACE_COMPONENT_ALPHA: {
-      nsRefPtr<gfxWindowsSurface> onBlack = opaqueRenderer.Begin(this);
-      nsRefPtr<gfxWindowsSurface> onWhite = opaqueRendererOnWhite.Begin(this);
+      nsRefPtr<gfxWindowsSurface> onBlack = opaqueRenderer.Begin(this, xres, yres);
+      nsRefPtr<gfxWindowsSurface> onWhite = opaqueRendererOnWhite.Begin(this, xres, yres);
       if (onBlack && onWhite) {
-        FillSurface(onBlack, aRegion, bounds.TopLeft(), gfxRGBA(0.0, 0.0, 0.0, 1.0));
-        FillSurface(onWhite, aRegion, bounds.TopLeft(), gfxRGBA(1.0, 1.0, 1.0, 1.0));
+        FillSurface(onBlack, aRegion, bounds.TopLeft(), gfxRGBA(0.0, 0.0, 0.0, 1.0), xres, yres);
+        FillSurface(onWhite, aRegion, bounds.TopLeft(), gfxRGBA(1.0, 1.0, 1.0, 1.0), xres, yres);
         gfxASurface* surfaces[2] = { onBlack.get(), onWhite.get() };
         destinationSurface = new gfxTeeSurface(surfaces, NS_ARRAY_LENGTH(surfaces));
         // Using this surface as a source will likely go horribly wrong, since
         // only the onBlack surface will really be used, so alpha information will
         // be incorrect.
         destinationSurface->SetAllowUseAsSource(PR_FALSE);
       }
       break;
     }
   }
 
   if (!destinationSurface)
     return;
 
   nsRefPtr<gfxContext> context = new gfxContext(destinationSurface);
+  // Draw content scaled at our current resolution.
+  context->Scale(xres, yres);
   context->Translate(gfxPoint(-bounds.x, -bounds.y));
+  aRegion.ExtendForScaling(xres, yres);
   LayerManagerD3D9::CallbackInfo cbInfo = mD3DManager->GetCallbackInfo();
   cbInfo.Callback(this, context, aRegion, nsIntRegion(), cbInfo.CallbackData);
 
   nsAutoTArray<IDirect3DTexture9*,2> srcTextures;
   nsAutoTArray<IDirect3DTexture9*,2> destTextures;
   switch (aMode)
   {
     case SURFACE_OPAQUE:
@@ -467,18 +491,17 @@ ThebesLayerD3D9::DrawRegion(const nsIntR
       break;
 
     case SURFACE_SINGLE_CHANNEL_ALPHA: {
       D3DLOCKED_RECT r;
       tmpTexture->LockRect(0, &r, NULL, 0);
 
       nsRefPtr<gfxImageSurface> imgSurface =
         new gfxImageSurface((unsigned char *)r.pBits,
-                            gfxIntSize(bounds.width,
-                                       bounds.height),
+                            scaledSize,
                             r.Pitch,
                             gfxASurface::ImageFormatARGB32);
 
       if (destinationSurface) {
         nsRefPtr<gfxContext> context = new gfxContext(imgSurface);
         context->SetSource(destinationSurface);
         context->SetOperator(gfxContext::OPERATOR_SOURCE);
         context->Paint();
@@ -500,56 +523,92 @@ ThebesLayerD3D9::DrawRegion(const nsIntR
       destTextures.AppendElement(mTexture);
       srcTextures.AppendElement(opaqueRendererOnWhite.GetTexture());
       destTextures.AppendElement(mTextureOnWhite);
       break;
     }
   }
   NS_ASSERTION(srcTextures.Length() == destTextures.Length(), "Mismatched lengths");
 
+  // Copy to the texture. We need to scale these rectangles since the visible
+  // region is in unscaled units, and the texture sizes have been scaled.
   for (PRUint32 i = 0; i < srcTextures.Length(); ++i) {
     nsRefPtr<IDirect3DSurface9> srcSurface;
     nsRefPtr<IDirect3DSurface9> dstSurface;
 
     destTextures[i]->GetSurfaceLevel(0, getter_AddRefs(dstSurface));
     srcTextures[i]->GetSurfaceLevel(0, getter_AddRefs(srcSurface));
 
     nsIntRegionRectIterator iter(aRegion);
     const nsIntRect *iterRect;
     while ((iterRect = iter.Next())) {
       RECT rect;
-      rect.left = iterRect->x - bounds.x;
-      rect.top = iterRect->y - bounds.y;
-      rect.right = rect.left + iterRect->width;
-      rect.bottom = rect.top + iterRect->height;
+      rect.left = UINT(floor((iterRect->x - bounds.x) * xres));
+      rect.top = UINT(floor((iterRect->y - bounds.y) * yres));
+      rect.right = rect.left +  UINT(ceil(iterRect->width * xres));
+      rect.bottom = rect.top +  UINT(ceil(iterRect->height * yres));
       POINT point;
-      point.x = iterRect->x - visibleRect.x;
-      point.y = iterRect->y - visibleRect.y;
+      point.x =  UINT(floor((iterRect->x - visibleRect.x) * xres));
+      point.y =  UINT(floor((iterRect->y - visibleRect.y) * yres));
       device()->UpdateSurface(srcSurface, &rect, dstSurface, &point);
     }
   }
 }
 
 void
 ThebesLayerD3D9::CreateNewTextures(const gfxIntSize &aSize,
                                    SurfaceMode aMode)
 {
   if (aSize.width == 0 || aSize.height == 0) {
     // Nothing to do.
     return;
   }
+
+  // Scale the requested size (in unscaled units) to the actual
+  // texture size we require.
+  gfxIntSize scaledSize;
+  float xres, yres;
+  GetDesiredResolutions(xres, yres);
+  scaledSize.width = PRInt32(ceil(aSize.width * xres));
+  scaledSize.height = PRInt32(ceil(aSize.height * yres));
 
   mTexture = nsnull;
   mTextureOnWhite = nsnull;
-  device()->CreateTexture(aSize.width, aSize.height, 1,
+  device()->CreateTexture(scaledSize.width, scaledSize.height, 1,
                           D3DUSAGE_RENDERTARGET,
                           aMode != SURFACE_SINGLE_CHANNEL_ALPHA ? D3DFMT_X8R8G8B8 : D3DFMT_A8R8G8B8,
                           D3DPOOL_DEFAULT, getter_AddRefs(mTexture), NULL);
   if (aMode == SURFACE_COMPONENT_ALPHA) {
-    device()->CreateTexture(aSize.width, aSize.height, 1,
+    device()->CreateTexture(scaledSize.width, scaledSize.height, 1,
                             D3DUSAGE_RENDERTARGET,
                             D3DFMT_X8R8G8B8,
                             D3DPOOL_DEFAULT, getter_AddRefs(mTextureOnWhite), NULL);
   }
+
+  mXResolution = xres;
+  mYResolution = yres;
+}
+
+void 
+ThebesLayerD3D9::GetDesiredResolutions(float& aXRes, float& aYRes)
+{
+  const gfx3DMatrix& transform = GetLayer()->GetEffectiveTransform();
+  gfxMatrix transform2d;
+  if (transform.Is2D(&transform2d)) {     
+    //Scale factors are normalized to a power of 2 to reduce the number of resolution changes
+    gfxSize scale = transform2d.ScaleFactors(PR_TRUE);
+    aXRes = gfxUtils::ClampToScaleFactor(scale.width);
+    aYRes = gfxUtils::ClampToScaleFactor(scale.height);
+  } else {
+    aXRes = 1.0;
+    aYRes = 1.0;
+  }
+}
+
+bool 
+ThebesLayerD3D9::ResolutionChanged(float aXRes, float aYRes)
+{
+  return aXRes != mXResolution ||
+         aYRes != mYResolution;
 }
 
 } /* namespace layers */
 } /* namespace mozilla */
--- a/gfx/layers/d3d9/ThebesLayerD3D9.h
+++ b/gfx/layers/d3d9/ThebesLayerD3D9.h
@@ -91,21 +91,32 @@ private:
   void UpdateTextures(SurfaceMode aMode);
 
   /* Render the rectangles of mVisibleRegion with D3D9 using the currently
    * bound textures, target, shaders, etc.
    */
   void RenderVisibleRegion();
 
   /* Have a region of our layer drawn */
-  void DrawRegion(const nsIntRegion &aRegion, SurfaceMode aMode);
+  void DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode);
 
   /* Create a new texture */
   void CreateNewTextures(const gfxIntSize &aSize, SurfaceMode aMode);
 
   void CopyRegion(IDirect3DTexture9* aSrc, const nsIntPoint &aSrcOffset,
                   IDirect3DTexture9* aDest, const nsIntPoint &aDestOffset,
-                  const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion);
+                  const nsIntRegion &aCopyRegion, nsIntRegion* aValidRegion,
+                  float aXRes, float aYRes);
+
+  /**
+   * Calculate the desired texture resolution based on
+   * the layer managers resolution, and the current
+   * transforms scale factor.
+   */
+  void GetDesiredResolutions(float& aXRes, float& aYRes);
+
+  /* Check if the current texture resolution matches */
+  bool ResolutionChanged(float aXRes, float aYRes);
 };
 
 } /* layers */
 } /* mozilla */
 #endif /* GFX_THEBESLAYERD3D9_H */