Bug 417967. Align subframe drawing so that drawing at (0,0) relative to the root view's origin is aligned to a pixel boundary. r+sr=bzbarsky
authorroc+@cs.cmu.edu
Sun, 24 Feb 2008 17:39:34 -0800
changeset 12189 066bca18f64aaa6262208b6a94fd2b7794fe8497
parent 12188 2624104eb4191188b5598656a021317be898d5e4
child 12190 e77169593c7aec4f7ff349afc96cf41ff9805de2
push idunknown
push userunknown
push dateunknown
bugs417967
milestone1.9b4pre
Bug 417967. Align subframe drawing so that drawing at (0,0) relative to the root view's origin is aligned to a pixel boundary. r+sr=bzbarsky
layout/base/nsDisplayList.h
layout/base/nsLayoutUtils.h
view/public/nsIViewObserver.h
view/src/nsViewManager.cpp
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -653,19 +653,20 @@ public:
    * painting and also display items that maintain child lists.
    * 
    * @param aVisibleRegion the area that is visible, relative to the
    * reference frame; on return, this contains the area visible under the list
    */
   void OptimizeVisibility(nsDisplayListBuilder* aBuilder, nsRegion* aVisibleRegion);
   /**
    * Paint the list to the rendering context. We assume that (0,0) in aCtx
-   * corresponds to the origin of the reference frame. The rectangle in
-   * aDirtyRect is painted, which *must* be contained in the dirty rect
-   * used to construct the display list.
+   * corresponds to the origin of the reference frame. For best results,
+   * aCtx's current transform should make (0,0) pixel-aligned. The
+   * rectangle in aDirtyRect is painted, which *must* be contained in the
+   * dirty rect used to construct the display list.
    */
   void Paint(nsDisplayListBuilder* aBuilder, nsIRenderingContext* aCtx,
              const nsRect& aDirtyRect) const;
   /**
    * Find the topmost display item that returns a non-null frame, and return
    * the frame.
    */
   nsIFrame* HitTest(nsDisplayListBuilder* aBuilder, nsPoint aPt,
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -389,17 +389,18 @@ public:
    */
   static nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt,
                                     PRBool aShouldIgnoreSuppression = PR_FALSE);
 
   /**
    * Given aFrame, the root frame of a stacking context, paint it and its
    * descendants to aRenderingContext. 
    * @param aRenderingContext a rendering context translated so that (0,0)
-   * is the origin of aFrame
+   * is the origin of aFrame; for best results, (0,0) should transform
+   * to pixel-aligned coordinates
    * @param aDirtyRegion the region that must be painted, in the coordinates
    * of aFrame
    * @param aBackground paint the dirty area with this color before drawing
    * the actual content; pass NS_RGBA(0,0,0,0) to draw no background
    */
   static nsresult PaintFrame(nsIRenderingContext* aRenderingContext, nsIFrame* aFrame,
                              const nsRegion& aDirtyRegion, nscolor aBackground);
 
--- a/view/public/nsIViewObserver.h
+++ b/view/public/nsIViewObserver.h
@@ -54,17 +54,20 @@ class nsIViewObserver : public nsISuppor
 {
 public:
   
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IVIEWOBSERVER_IID)
 
   /* called when the observer needs to paint. This paints the entire
    * frame subtree rooted at the view, including frame subtrees from
    * subdocuments.
-   * @param aRenderingContext rendering context to paint to
+   * @param aRenderingContext rendering context to paint to; the origin
+   * of the view is painted at (0,0) in the rendering context's current
+   * transform. For best results this should transform to pixel-aligned
+   * coordinates.
    * @param aDirtyRegion the region to be painted, in the coordinates of aRootView
    * @return error status
    */
   NS_IMETHOD Paint(nsIView*             aRootView,
                    nsIRenderingContext* aRenderingContext,
                    const nsRegion&      aDirtyRegion) = 0;
 
   /**
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -591,19 +591,35 @@ void nsViewManager::AddCoveringWidgetsTo
 void nsViewManager::RenderViews(nsView *aView, nsIRenderingContext& aRC,
                                 const nsRegion& aRegion)
 {
   if (mObserver) {
     nsView* displayRoot = GetDisplayRootFor(aView);
     nsPoint offsetToRoot = aView->GetOffsetTo(displayRoot); 
     nsRegion damageRegion(aRegion);
     damageRegion.MoveBy(offsetToRoot);
-    
+
+    gfxContext* ctx = aRC.ThebesContext();
+    nsCOMPtr<nsIDeviceContext> dc;
+    aRC.GetDeviceContext(*getter_AddRefs(dc));
+    double appPerDev = dc->AppUnitsPerDevPixel();
+    gfxRect r(-offsetToRoot.x/appPerDev, -offsetToRoot.y/appPerDev, 1.0, 1.0);
+
     aRC.PushState();
-    aRC.Translate(-offsetToRoot.x, -offsetToRoot.y);
+    // Translate by the pixel-snapped offsetToRoot so that aRC's (0,0) will
+    // be aligned to pixel boundaries. We use gfx pixel-snapping here to
+    // ensure that the snapping we do here is consistent with other gfx
+    // snapping. For example if someone drew a border around the outside
+    // of aView, we want our (0,0) to be the inside top-left of that
+    // border.
+    if (ctx->UserToDevicePixelSnapped(r)) {
+      ctx->Translate(ctx->DeviceToUser(r).pos);
+    } else {
+      aRC.Translate(-offsetToRoot.x, -offsetToRoot.y);
+    }
     mObserver->Paint(displayRoot, &aRC, damageRegion);
     aRC.PopState();
   }
 }
 
 void nsViewManager::ProcessPendingUpdates(nsView* aView, PRBool aDoInvalidate)
 {
   NS_ASSERTION(IsRootVM(), "Updates will be missed");