Bug 586683 - Part 2b - Add resolution handling to ThebesLayerOGL. r=joe a=blocking2.0
authorMatt Woodrow <mwoodrow@mozilla.com>
Wed, 09 Feb 2011 09:37:47 +1300
changeset 62163 acec86b21f5ee4b38145d92cc182a206be0e3299
parent 62162 b0512b9a8c11ae974391a714753aca994857a391
child 62164 9feb4c4ae2e7d5fa195b12cfb538e8a07a017149
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersjoe, blocking2.0
bugs586683
milestone2.0b12pre
Bug 586683 - Part 2b - Add resolution handling to ThebesLayerOGL. r=joe a=blocking2.0
gfx/layers/opengl/ThebesLayerOGL.cpp
--- a/gfx/layers/opengl/ThebesLayerOGL.cpp
+++ b/gfx/layers/opengl/ThebesLayerOGL.cpp
@@ -151,17 +151,19 @@ public:
   typedef ThebesLayerBuffer::PaintState PaintState;
 
   ThebesLayerBufferOGL(ThebesLayer* aLayer, LayerOGL* aOGLLayer)
     : mLayer(aLayer)
     , mOGLLayer(aOGLLayer)
   {}
   virtual ~ThebesLayerBufferOGL() {}
 
-  virtual PaintState BeginPaint(ContentType aContentType) = 0;
+  virtual PaintState BeginPaint(ContentType aContentType,
+                                float aXResolution,
+                                float aYResolution) = 0;
 
   void RenderTo(const nsIntPoint& aOffset, LayerManagerOGL* aManager);
 
   nsIntSize GetSize() {
     if (mTexImage)
       return mTexImage->GetSize();
     return nsIntSize(0, 0);
   }
@@ -251,22 +253,19 @@ ThebesLayerBufferOGL::RenderTo(const nsI
       DEBUG_GL_ERROR_CHECK(gl());
 
       quadRect.MoveBy(-GetOriginOffset());
 
       // The buffer rect and rotation are resolution-neutral; with a
       // non-1.0 resolution, only the texture size is scaled by the
       // resolution.  So map the quadrent rect into the space scaled to
       // the texture size and let GL do the rest.
-      gfxRect sqr(quadRect.x, quadRect.y, quadRect.width, quadRect.height);
-      sqr.Scale(xres, yres);
-      sqr.Round();
-      nsIntRect scaledQuadRect(sqr.pos.x, sqr.pos.y, sqr.size.width, sqr.size.height);
+      quadRect.ScaleRoundOut(xres, yres);
 
-      BindAndDrawQuadWithTextureRect(gl(), program, scaledQuadRect,
+      BindAndDrawQuadWithTextureRect(gl(), program, quadRect,
                                      mTexImage->GetSize(),
                                      mTexImage->GetWrapMode());
       DEBUG_GL_ERROR_CHECK(gl());
     }
   }
 
   if (mTexImageOnWhite) {
     // Restore defaults
@@ -288,20 +287,25 @@ public:
   SurfaceBufferOGL(ThebesLayerOGL* aLayer)
     : ThebesLayerBufferOGL(aLayer, aLayer)
     , ThebesLayerBuffer(SizedToVisibleBounds)
   {
   }
   virtual ~SurfaceBufferOGL() {}
 
   // ThebesLayerBufferOGL interface
-  virtual PaintState BeginPaint(ContentType aContentType)
+  virtual PaintState BeginPaint(ContentType aContentType, 
+                                float aXResolution, 
+                                float aYResolution)
   {
     // Let ThebesLayerBuffer do all the hard work for us! :D
-    return ThebesLayerBuffer::BeginPaint(mLayer, aContentType, 1.0, 1.0);
+    return ThebesLayerBuffer::BeginPaint(mLayer, 
+                                         aContentType, 
+                                         aXResolution, 
+                                         aYResolution);
   }
 
   // ThebesLayerBuffer interface
   virtual already_AddRefed<gfxASurface>
   CreateBuffer(ContentType aType, const nsIntSize& aSize)
   {
     NS_ASSERTION(gfxASurface::CONTENT_ALPHA != aType,"ThebesBuffer has color");
 
@@ -325,17 +329,19 @@ class BasicBufferOGL : public ThebesLaye
 public:
   BasicBufferOGL(ThebesLayerOGL* aLayer)
     : ThebesLayerBufferOGL(aLayer, aLayer)
     , mBufferRect(0,0,0,0)
     , mBufferRotation(0,0)
   {}
   virtual ~BasicBufferOGL() {}
 
-  virtual PaintState BeginPaint(ContentType aContentType);
+  virtual PaintState BeginPaint(ContentType aContentType,
+                                float aXResolution,
+                                float aYResolution);
 
 protected:
   enum XSide {
     LEFT, RIGHT
   };
   enum YSide {
     TOP, BOTTOM
   };
@@ -368,47 +374,66 @@ BasicBufferOGL::GetQuadrantRectangle(XSi
   nsIntPoint quadrantTranslation = -mBufferRotation;
   quadrantTranslation.x += aXSide == LEFT ? mBufferRect.width : 0;
   quadrantTranslation.y += aYSide == TOP ? mBufferRect.height : 0;
   return mBufferRect + quadrantTranslation;
 }
 
 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();
 }
 
+static nsIntSize
+ScaledSize(const nsIntSize& aSize, float aXScale, float aYScale)
+{
+  if (aXScale == 1.0 && aYScale == 1.0) {
+    return aSize;
+  }
+
+  nsIntRect rect(0, 0, aSize.width, aSize.height);
+  rect.ScaleRoundOut(aXScale, aYScale);
+  return rect.Size();
+}
+
 BasicBufferOGL::PaintState
-BasicBufferOGL::BeginPaint(ContentType aContentType)
+BasicBufferOGL::BeginPaint(ContentType aContentType,
+                           float aXResolution,
+                           float aYResolution)
 {
   PaintState result;
 
   result.mRegionToDraw.Sub(mLayer->GetVisibleRegion(), mLayer->GetValidRegion());
-
+  
+  float curXRes = mLayer->GetXResolution();
+  float curYRes = mLayer->GetYResolution();
   Layer::SurfaceMode mode = mLayer->GetSurfaceMode();
 
   if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
     mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
 #else
     if (!mLayer->GetParent() || !mLayer->GetParent()->SupportsComponentAlphaChildren()) {
       mode = Layer::SURFACE_SINGLE_CHANNEL_ALPHA;
     } else {
       aContentType = gfxASurface::CONTENT_COLOR;
     }
 #endif
   }
 
   if (!mTexImage || mTexImage->GetContentType() != aContentType ||
+      aXResolution != curXRes || aYResolution != curYRes ||
       (mode == Layer::SURFACE_COMPONENT_ALPHA) != (mTexImageOnWhite != nsnull)) {
     // We're effectively clearing the valid region, so we need to draw
     // the entire visible region now.
     //
     // XXX/cjones: a possibly worthwhile optimization to keep in mind
     // is to re-use buffers when the resolution and visible region
     // have changed in such a way that the buffer size stays the same.
     // It might make even more sense to allocate buffers from a
@@ -427,21 +452,25 @@ BasicBufferOGL::BeginPaint(ContentType a
 
   nsIntRect visibleBounds = mLayer->GetVisibleRegion().GetBounds();
   if (visibleBounds.width > gl()->GetMaxTextureSize() ||
       visibleBounds.height > gl()->GetMaxTextureSize()) {
     return result;
   }
 
   nsIntRect drawBounds = result.mRegionToDraw.GetBounds();
+  nsIntSize destBufferDims = ScaledSize(visibleBounds.Size(),
+                                        aXResolution, aYResolution);
   nsRefPtr<TextureImage> destBuffer;
   nsRefPtr<TextureImage> destBufferOnWhite;
   nsIntRect destBufferRect;
 
   if (visibleBounds.Size() <= mBufferRect.Size()) {
+    NS_ASSERTION(curXRes == aXResolution && curYRes == aYResolution,
+                 "resolution changes must clear the buffer!");
     // The current buffer is big enough to hold the visible area.
     if (mBufferRect.Contains(visibleBounds)) {
       // We don't need to adjust mBufferRect.
       destBufferRect = mBufferRect;
     } else {
       // The buffer's big enough but doesn't contain everything that's
       // going to be visible. We'll move it.
       destBufferRect = nsIntRect(visibleBounds.TopLeft(), mBufferRect.Size());
@@ -463,23 +492,23 @@ BasicBufferOGL::BeginPaint(ContentType a
           (drawBounds.y < yBoundary && yBoundary < drawBounds.YMost())) {
         // The stuff we need to redraw will wrap around an edge of the
         // buffer, so we will need to do a self-copy
         // If mBufferRotation == nsIntPoint(0,0) we could do a real
         // self-copy but we're not going to do that in GL yet.
         // We can't do a real self-copy because the buffer is rotated.
         // So allocate a new buffer for the destination.
         destBufferRect = visibleBounds;
-        destBuffer = CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+        destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
         DEBUG_GL_ERROR_CHECK(gl());
         if (!destBuffer)
           return result;
         if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
-          destBufferOnWhite = 
-            CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+          destBufferOnWhite =
+            CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
           DEBUG_GL_ERROR_CHECK(gl());
           if (!destBufferOnWhite)
             return result;
         }
       } else {
         mBufferRect = destBufferRect;
         mBufferRotation = newRotation;
       }
@@ -488,24 +517,24 @@ BasicBufferOGL::BeginPaint(ContentType a
       // will be redrawn, so we don't need to copy anything, so we don't
       // set destBuffer.
       mBufferRect = destBufferRect;
       mBufferRotation = nsIntPoint(0,0);
     }
   } else {
     // The buffer's not big enough, so allocate a new one
     destBufferRect = visibleBounds;
-    destBuffer = CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+    destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
     DEBUG_GL_ERROR_CHECK(gl());
     if (!destBuffer)
       return result;
 
     if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
       destBufferOnWhite = 
-        CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+        CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
       DEBUG_GL_ERROR_CHECK(gl());
       if (!destBufferOnWhite)
         return result;
     }
   }
 
   if (!destBuffer && !mTexImage) {
     return result;
@@ -518,32 +547,35 @@ BasicBufferOGL::BeginPaint(ContentType a
       if (mOGLLayer->OGLManager()->FBOTextureTarget() == LOCAL_GL_TEXTURE_2D) {
         nsIntRect overlap;
         overlap.IntersectRect(mBufferRect, destBufferRect);
 
         nsIntRect srcRect(overlap), dstRect(overlap);
         srcRect.MoveBy(- mBufferRect.TopLeft() + mBufferRotation);
         dstRect.MoveBy(- destBufferRect.TopLeft());
 
-        destBuffer->Resize(destBufferRect.Size());
+        nsIntSize size = ScaledSize(destBufferRect.Size(), aXResolution, aYResolution);
+        destBuffer->Resize(size);
+        srcRect.ScaleRoundOut(aXResolution, aYResolution);
+        dstRect.ScaleRoundOut(aXResolution, aYResolution);
 
         gl()->BlitTextureImage(mTexImage, srcRect,
                                destBuffer, dstRect);
         if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
-          destBufferOnWhite->Resize(destBufferRect.Size());
+          destBufferOnWhite->Resize(size);
           gl()->BlitTextureImage(mTexImageOnWhite, srcRect,
                                  destBufferOnWhite, dstRect);
         }
       } else {
         // can't blit, just draw everything
         destBufferRect = visibleBounds;
-        destBuffer = CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+        destBuffer = CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
         if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
           destBufferOnWhite = 
-            CreateClampOrRepeatTextureImage(gl(), visibleBounds.Size(), aContentType);
+            CreateClampOrRepeatTextureImage(gl(), destBufferDims, aContentType);
         }
       }
     }
 
     mTexImage = destBuffer.forget();
     if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
       mTexImageOnWhite = destBufferOnWhite.forget();
     }
@@ -559,30 +591,31 @@ BasicBufferOGL::BeginPaint(ContentType a
   PRInt32 xBoundary = mBufferRect.XMost() - mBufferRotation.x;
   PRInt32 yBoundary = mBufferRect.YMost() - mBufferRotation.y;
   XSide sideX = drawBounds.XMost() <= xBoundary ? RIGHT : LEFT;
   YSide sideY = drawBounds.YMost() <= yBoundary ? BOTTOM : TOP;
   nsIntRect quadrantRect = GetQuadrantRectangle(sideX, sideY);
   NS_ASSERTION(quadrantRect.Contains(drawBounds), "Messed up quadrants");
 
   nsIntPoint offset = -nsIntPoint(quadrantRect.x, quadrantRect.y);
-  
+
   // Make the region to draw relative to the buffer, before
   // passing to BeginUpdate.
   result.mRegionToDraw.MoveBy(offset);
+  result.mRegionToDraw.ScaleRoundOut(aXResolution, aYResolution);
   // BeginUpdate is allowed to modify the given region,
   // if it wants more to be repainted than we request.
   if (mode == Layer::SURFACE_COMPONENT_ALPHA) {
     nsIntRegion drawRegionCopy = result.mRegionToDraw;
     gfxASurface *onBlack = mTexImage->BeginUpdate(drawRegionCopy);
     gfxASurface *onWhite = mTexImageOnWhite->BeginUpdate(result.mRegionToDraw);
     NS_ASSERTION(result.mRegionToDraw == drawRegionCopy,
                  "BeginUpdate should always modify the draw region in the same way!");
-    FillSurface(onBlack, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(0.0, 0.0, 0.0, 1.0));
-    FillSurface(onWhite, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(1.0, 1.0, 1.0, 1.0));
+    FillSurface(onBlack, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(0.0, 0.0, 0.0, 1.0), aXResolution, aYResolution);
+    FillSurface(onWhite, result.mRegionToDraw, nsIntPoint(0,0), gfxRGBA(1.0, 1.0, 1.0, 1.0), aXResolution, aYResolution);
     gfxASurface* surfaces[2] = { onBlack, onWhite };
     nsRefPtr<gfxTeeSurface> surf = new gfxTeeSurface(surfaces, NS_ARRAY_LENGTH(surfaces));
 
     // XXX If the device offset is set on the individual surfaces instead of on
     // the tee surface, we render in the wrong place. Why?
     gfxPoint deviceOffset = onBlack->GetDeviceOffset();
     onBlack->SetDeviceOffset(gfxPoint(0, 0));
     onWhite->SetDeviceOffset(gfxPoint(0, 0));
@@ -601,20 +634,25 @@ BasicBufferOGL::BeginPaint(ContentType a
       result.mContext->Fill();
       result.mContext->SetOperator(gfxContext::OPERATOR_OVER);
     }
   }
   if (!result.mContext) {
     NS_WARNING("unable to get context for update");
     return result;
   }
+  result.mContext->Scale(aXResolution, aYResolution);
   result.mContext->Translate(-gfxPoint(quadrantRect.x, quadrantRect.y));
   // Move rgnToPaint back into position so that the thebes callback
   // gets the right coordintes.
+  result.mRegionToDraw.ScaleRoundOut(1/aXResolution, 1/aYResolution);
   result.mRegionToDraw.MoveBy(-offset);
+  // Round our region out to values that will scale cleanly by the given
+  // resolution.
+  result.mRegionToDraw.ExtendForScaling(aXResolution, aYResolution);
   
   return result;
 }
 
 ThebesLayerOGL::ThebesLayerOGL(LayerManagerOGL *aManager)
   : ThebesLayer(aManager, nsnull)
   , LayerOGL(aManager)
   , mBuffer(nsnull)
@@ -680,21 +718,33 @@ ThebesLayerOGL::RenderLayer(int aPreviou
   NS_ABORT_IF_FALSE(mBuffer, "should have a buffer here");
 
   mOGLManager->MakeCurrent();
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
 
   TextureImage::ContentType contentType =
     CanUseOpaqueSurface() ? gfxASurface::CONTENT_COLOR :
                             gfxASurface::CONTENT_COLOR_ALPHA;
-  Buffer::PaintState state = mBuffer->BeginPaint(contentType);
+
+  const gfx3DMatrix& transform = GetEffectiveTransform();
+  gfxMatrix transform2d;
+  gfxSize scale(1.0, 1.0);
+  if (transform.Is2D(&transform2d)) {
+    scale = transform2d.ScaleFactors(PR_TRUE);
+  }
+  float paintXRes = gfxUtils::ClampToScaleFactor(scale.width);
+  float paintYRes = gfxUtils::ClampToScaleFactor(scale.height);
+
+  Buffer::PaintState state = mBuffer->BeginPaint(contentType, paintXRes, paintYRes);
   mValidRegion.Sub(mValidRegion, state.mRegionToInvalidate);
 
   if (state.mContext) {
     state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
+    mXResolution = paintXRes;
+    mYResolution = paintYRes;
 
     LayerManager::DrawThebesLayerCallback callback =
       mOGLManager->GetThebesLayerCallback();
     if (!callback) {
       NS_ERROR("GL should never need to update ThebesLayers in an empty transaction");
     } else {
       void* callbackData = mOGLManager->GetThebesLayerCallbackData();
       callback(this, state.mContext, state.mRegionToDraw,
@@ -731,17 +781,17 @@ ThebesLayerOGL::IsEmpty()
 
 class ShadowBufferOGL : public ThebesLayerBufferOGL
 {
 public:
   ShadowBufferOGL(ShadowThebesLayerOGL* aLayer)
     : ThebesLayerBufferOGL(aLayer, aLayer)
   {}
 
-  virtual PaintState BeginPaint(ContentType aContentType) {
+  virtual PaintState BeginPaint(ContentType aContentType, float, float) {
     NS_RUNTIMEABORT("can't BeginPaint for a shadow layer");
     return PaintState();
   }
 
   void
   CreateTexture(ContentType aType, const nsIntSize& aSize)
   {
     NS_ASSERTION(gfxASurface::CONTENT_ALPHA != aType,"ThebesBuffer has color");