OSDN Git Service

implement display projection clipping in h/w composer
authorMathias Agopian <mathias@google.com>
Fri, 1 Mar 2013 01:12:07 +0000 (17:12 -0800)
committerMathias Agopian <mathias@google.com>
Fri, 1 Mar 2013 02:43:04 +0000 (18:43 -0800)
- cropping to the projection's "viewport" is "simply"
  accomplished by intersecting it with the window crop
  expressed in layerstack space.

Bug: 7149437
Change-Id: I0e90b3f37945292314b5d78a8f134935967e8053

services/surfaceflinger/LayerBase.cpp
services/surfaceflinger/Transform.cpp
services/surfaceflinger/Transform.h

index 572e7c8..db2b20e 100644 (file)
@@ -281,6 +281,14 @@ uint32_t LayerBase::getContentTransform() const {
 }
 
 Rect LayerBase::computeCrop(const sp<const DisplayDevice>& hw) const {
+    /*
+     * The way we compute the crop (aka. texture coordinates when we have a
+     * Layer) produces a different output from the GL code in
+     * drawWithOpenGL() due to HWC being limited to integers. The difference
+     * can be large if getContentTransform() contains a large scale factor.
+     * See comments in drawWithOpenGL() for more details.
+     */
+
     // the content crop is the area of the content that gets scaled to the
     // layer's size.
     Rect crop(getContentCrop());
@@ -288,7 +296,21 @@ Rect LayerBase::computeCrop(const sp<const DisplayDevice>& hw) const {
     // the active.crop is the area of the window that gets cropped, but not
     // scaled in any ways.
     const State& s(drawingState());
-    Rect activeCrop(s.active.crop);
+
+    // apply the projection's clipping to the window crop in
+    // layerstack space, and convert-back to layer space.
+    // if there are no window scaling (or content scaling) involved,
+    // this operation will map to full pixels in the buffer.
+    // NOTE: should we revert to GL composition if a scaling is involved
+    // since it cannot be represented in the HWC API?
+    Rect activeCrop(s.transform.transform(s.active.crop));
+    activeCrop.intersect(hw->getViewport(), &activeCrop);
+    activeCrop = s.transform.inverse().transform(activeCrop);
+
+    // paranoia: make sure the window-crop is constrained in the
+    // window's bounds
+    activeCrop.intersect(Rect(s.active.w, s.active.h), &activeCrop);
+
     if (!activeCrop.isEmpty()) {
         // Transform the window crop to match the buffer coordinate system,
         // which means using the inverse of the current transform set on the
@@ -350,6 +372,7 @@ void LayerBase::setGeometry(
     // here we're guaranteed that the layer's transform preserves rects
 
     Rect frame(s.transform.transform(computeBounds()));
+    frame.intersect(hw->getViewport(), &frame);
     const Transform& tr(hw->getTransform());
     layer.setFrame(tr.transform(frame));
     layer.setCrop(computeCrop(hw));
@@ -464,10 +487,22 @@ void LayerBase::drawWithOpenGL(const sp<const DisplayDevice>& hw, const Region&
         GLfloat v;
     };
 
-    Rect win(s.active.w, s.active.h);
-    if (!s.active.crop.isEmpty()) {
-        win.intersect(s.active.crop, &win);
-    }
+
+    /*
+     * NOTE: the way we compute the texture coordinates here produces
+     * different results than when we take the HWC path -- in the later case
+     * the "source crop" is rounded to texel boundaries.
+     * This can produce significantly different results when the texture
+     * is scaled by a large amount.
+     *
+     * The GL code below is more logical (imho), and the difference with
+     * HWC is due to a limitation of the HWC API to integers -- a question
+     * is suspend is wether we should ignore this problem or revert to
+     * GL composition when a buffer scaling is applied (maybe with some
+     * minimal value)? Or, we could make GL behave like HWC -- but this feel
+     * like more of a hack.
+     */
+    const Rect win(computeBounds());
 
     GLfloat left   = GLfloat(win.left)   / GLfloat(s.active.w);
     GLfloat top    = GLfloat(win.top)    / GLfloat(s.active.h);
index aca90e0..315720e 100644 (file)
@@ -298,6 +298,44 @@ uint32_t Transform::type() const
     return mType;
 }
 
+Transform Transform::inverse() const {
+    // our 3x3 matrix is always of the form of a 2x2 transformation
+    // followed by a translation: T*M, therefore:
+    // (T*M)^-1 = M^-1 * T^-1
+    Transform result;
+    if (mType <= TRANSLATE) {
+        // 1 0 x
+        // 0 1 y
+        // 0 0 1
+        result = *this;
+        result.mMatrix[2][0] = -result.mMatrix[2][0];
+        result.mMatrix[2][1] = -result.mMatrix[2][1];
+    } else {
+        // a c x
+        // b d y
+        // 0 0 1
+        const mat33& M(mMatrix);
+        const float a = M[0][0];
+        const float b = M[1][0];
+        const float c = M[0][1];
+        const float d = M[1][1];
+        const float x = M[2][0];
+        const float y = M[2][1];
+
+        Transform R, T;
+        const float idet = 1.0 / (a*d - b*c);
+        R.mMatrix[0][0] =  d*idet;    R.mMatrix[0][1] = -c*idet;
+        R.mMatrix[1][0] = -b*idet;    R.mMatrix[1][1] =  a*idet;
+        R.mType = mType &= ~TRANSLATE;
+
+        T.mMatrix[2][0] = -x;
+        T.mMatrix[2][1] = -y;
+        T.mType = TRANSLATE;
+        result =  R * T;
+    }
+    return result;
+}
+
 uint32_t Transform::getType() const {
     return type() & 0xFF;
 }
index 4fe261a..c4efade 100644 (file)
@@ -80,6 +80,8 @@ public:
             Rect    transform(const Rect& bounds) const;
             Transform operator * (const Transform& rhs) const;
 
+            Transform inverse() const;
+
             // for debugging
             void dump(const char* name) const;