OSDN Git Service

Fix quickReject's handling of AA ramp geometry
authorChris Craik <ccraik@google.com>
Wed, 31 Jul 2013 02:05:20 +0000 (19:05 -0700)
committerChris Craik <ccraik@google.com>
Fri, 2 Aug 2013 18:29:22 +0000 (11:29 -0700)
By having quickReject round out the window-space geometry bounds, we
prevent the AA perimeter (which falls outside the local bounds passed
in) from drawing outside the clip.

Change-Id: I8ee36be9039a9c47906815ee2f0dbaa5eb910b82

libs/hwui/DeferredDisplayList.h
libs/hwui/DisplayListOp.h
libs/hwui/OpenGLRenderer.cpp
libs/hwui/OpenGLRenderer.h
libs/hwui/Rect.h

index 33feb7e..6c5a847 100644 (file)
@@ -79,7 +79,7 @@ public:
 
     /**
      * Add a draw op into the DeferredDisplayList, reordering as needed (for performance) if
-     * disallowReorder is false, respecting draw order when overlaps occur
+     * disallowReorder is false, respecting draw order when overlaps occur.
      */
     void addDrawOp(OpenGLRenderer& renderer, DrawOp* op);
 
index 5ac2511..255b77f 100644 (file)
@@ -180,7 +180,11 @@ public:
      */
     virtual void onDefer(OpenGLRenderer& renderer, DeferInfo& deferInfo) {}
 
-    // returns true if bounds exist
+    /**
+     * Query the conservative, local bounds (unmapped) bounds of the op.
+     *
+     * returns true if bounds exist
+     */
     virtual bool getLocalBounds(Rect& localBounds) { return false; }
 
     // TODO: better refine localbounds usage
index bc00ce8..24cd5cb 100644 (file)
@@ -1414,6 +1414,10 @@ bool OpenGLRenderer::storeDisplayState(DeferredDisplayState& state, int stateDef
         if (!state.mBounds.isEmpty()) {
             currentMatrix.mapRect(state.mBounds);
             Rect clippedBounds(state.mBounds);
+            // NOTE: if we ever want to use this clipping info to drive whether the scissor
+            // is used, it should more closely duplicate the quickReject logic (in how it uses
+            // snapToPixelBoundaries)
+
             if(!clippedBounds.intersect(currentClip)) {
                 // quick rejected
                 return true;
@@ -1608,14 +1612,21 @@ const Rect& OpenGLRenderer::getClipBounds() {
 }
 
 bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, float bottom,
-        bool* clipRequired) {
+        bool snapOut, bool* clipRequired) {
     if (mSnapshot->isIgnored() || bottom <= top || right <= left) {
         return true;
     }
 
     Rect r(left, top, right, bottom);
     currentTransform().mapRect(r);
-    r.snapToPixelBoundaries();
+
+    if (snapOut) {
+        // snapOut is generally used to account for 1 pixel ramp (in window coordinates)
+        // outside of the provided rect boundaries in tessellated AA geometry
+        r.snapOutToPixelBoundaries();
+    } else {
+        r.snapToPixelBoundaries();
+    }
 
     Rect clipRect(*mSnapshot->clipRect);
     clipRect.snapToPixelBoundaries();
@@ -1628,17 +1639,21 @@ bool OpenGLRenderer::quickRejectNoScissor(float left, float top, float right, fl
 
 bool OpenGLRenderer::quickRejectPreStroke(float left, float top, float right, float bottom,
         SkPaint* paint) {
+    // AA geometry will likely have a ramp around it (not accounted for in local bounds). Snap out
+    // the final mapped rect to ensure correct clipping behavior for the ramp.
+    bool snapOut = paint->isAntiAlias();
+
     if (paint->getStyle() != SkPaint::kFill_Style) {
         float outset = paint->getStrokeWidth() * 0.5f;
-        return quickReject(left - outset, top - outset, right + outset, bottom + outset);
+        return quickReject(left - outset, top - outset, right + outset, bottom + outset, snapOut);
     } else {
-        return quickReject(left, top, right, bottom);
+        return quickReject(left, top, right, bottom, snapOut);
     }
 }
 
-bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
+bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom, bool snapOut) {
     bool clipRequired = false;
-    if (quickRejectNoScissor(left, top, right, bottom, &clipRequired)) {
+    if (quickRejectNoScissor(left, top, right, bottom, snapOut, &clipRequired)) {
         return true;
     }
 
@@ -3115,7 +3130,7 @@ status_t OpenGLRenderer::drawLayer(Layer* layer, float x, float y) {
 
     bool clipRequired = false;
     const bool rejected = quickRejectNoScissor(x, y,
-            x + layer->layer.getWidth(), y + layer->layer.getHeight(), &clipRequired);
+            x + layer->layer.getWidth(), y + layer->layer.getHeight(), false, &clipRequired);
 
     if (rejected) {
         if (transform && !transform->isIdentity()) {
index eb42540..2f8a2f0 100644 (file)
@@ -256,14 +256,15 @@ public:
     ANDROID_API const Rect& getClipBounds();
 
     /**
-     * Performs a quick reject but adjust the bounds to account for stroke width if necessary
+     * Performs a quick reject but adjust the bounds to account for stroke width if necessary,
+     * and handling snapOut for AA geometry.
      */
     bool quickRejectPreStroke(float left, float top, float right, float bottom, SkPaint* paint);
 
     /**
      * Returns false and sets scissor based upon bounds if drawing won't be clipped out
      */
-    bool quickReject(float left, float top, float right, float bottom);
+    bool quickReject(float left, float top, float right, float bottom, bool snapOut = false);
     bool quickReject(const Rect& bounds) {
         return quickReject(bounds.left, bounds.top, bounds.right, bounds.bottom);
     }
@@ -273,7 +274,7 @@ public:
      * clipRequired will be only set if not rejected
      */
     ANDROID_API bool quickRejectNoScissor(float left, float top, float right, float bottom,
-            bool* clipRequired = NULL);
+            bool snapOut = false, bool* clipRequired = NULL);
     bool quickRejectNoScissor(const Rect& bounds, bool* clipRequired = NULL) {
         return quickRejectNoScissor(bounds.left, bounds.top, bounds.right, bounds.bottom,
                 clipRequired);
@@ -340,6 +341,12 @@ public:
 
     SkPaint* filterPaint(SkPaint* paint);
 
+    /**
+     * Store the current display state (most importantly, the current clip and transform), and
+     * additionally map the state's bounds from local to window coordinates.
+     *
+     * Returns true if quick-rejected
+     */
     bool storeDisplayState(DeferredDisplayState& state, int stateDeferFlags);
     void restoreDisplayState(const DeferredDisplayState& state, bool skipClipRestore = false);
     void setupMergedMultiDraw(const Rect* clipRect);
index 7531769..7605307 100644 (file)
@@ -170,6 +170,20 @@ public:
         bottom += delta;
     }
 
+    /**
+     * Similar to snapToPixelBoundaries, but used for AA geometry with a ramp perimeter.
+     *
+     * We inset the data by a fudge factor of slightly over 1/16 (similar to when drawing non-AA
+     * lines) before rounding out so that insignificant amounts of ramp geometry (esp. from rounding
+     * errors) are ignored.
+     */
+    void snapOutToPixelBoundaries() {
+        left = floorf(left + 0.065f);
+        top = floorf(top + 0.065f);
+        right = ceilf(right - 0.065f);
+        bottom = ceilf(bottom - 0.065f);
+    }
+
     void snapToPixelBoundaries() {
         left = floorf(left + 0.5f);
         top = floorf(top + 0.5f);