implement display viewport and frame

note: viewport clipping is not implemented yet

Change-Id: I7fde7c4de075d409d95c48bb20ba8ee017f6f00a
diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp
index 16e5547..cf781d3 100644
--- a/services/surfaceflinger/DisplayDevice.cpp
+++ b/services/surfaceflinger/DisplayDevice.cpp
@@ -38,6 +38,7 @@
 #include "DisplayHardware/FramebufferSurface.h"
 #include "DisplayHardware/HWComposer.h"
 
+#include "clz.h"
 #include "DisplayDevice.h"
 #include "GLExtensions.h"
 #include "SurfaceFlinger.h"
@@ -84,8 +85,8 @@
       mPageFlipCount(),
       mSecureLayerVisible(false),
       mScreenAcquired(false),
-      mOrientation(),
-      mLayerStack(0)
+      mLayerStack(0),
+      mOrientation()
 {
     init(config);
 }
@@ -139,6 +140,8 @@
     mSurface = surface;
     mFormat  = format;
     mPageFlipCount = 0;
+    mViewport.makeInvalid();
+    mFrame.makeInvalid();
 
     // external displays are always considered enabled
     mScreenAcquired = (mType >= DisplayDevice::NUM_DISPLAY_TYPES);
@@ -192,12 +195,23 @@
     }
 }
 
-void DisplayDevice::makeCurrent(const sp<const DisplayDevice>& hw, EGLContext ctx) {
+EGLBoolean DisplayDevice::makeCurrent(EGLDisplay dpy,
+        const sp<const DisplayDevice>& hw, EGLContext ctx) {
+    EGLBoolean result = EGL_TRUE;
     EGLSurface sur = eglGetCurrentSurface(EGL_DRAW);
     if (sur != hw->mSurface) {
-        EGLDisplay dpy = eglGetCurrentDisplay();
-        eglMakeCurrent(dpy, hw->mSurface, hw->mSurface, ctx);
+        result = eglMakeCurrent(dpy, hw->mSurface, hw->mSurface, ctx);
+        if (result == EGL_TRUE) {
+            GLsizei w = hw->mDisplayWidth;
+            GLsizei h = hw->mDisplayHeight;
+            glViewport(0, 0, w, h);
+            glMatrixMode(GL_PROJECTION);
+            glLoadIdentity();
+            // put the origin in the left-bottom corner
+            glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
+        }
     }
+    return result;
 }
 
 // ----------------------------------------------------------------------------
@@ -223,10 +237,10 @@
 
 Region DisplayDevice::getDirtyRegion(bool repaintEverything) const {
     Region dirty;
-    const Transform& planeTransform(mGlobalTransform);
     if (repaintEverything) {
         dirty.set(getBounds());
     } else {
+        const Transform& planeTransform(mGlobalTransform);
         dirty = planeTransform.transform(this->dirtyRegion);
         dirty.andSelf(getBounds());
     }
@@ -284,18 +298,73 @@
     return NO_ERROR;
 }
 
-status_t DisplayDevice::setOrientation(int orientation) {
+void DisplayDevice::setOrientation(int orientation) {
+    mOrientation = orientation;
+    updateGeometryTransform();
+}
+
+void DisplayDevice::setViewport(const Rect& viewport) {
+    if (viewport.isValid()) {
+        mViewport = viewport;
+        updateGeometryTransform();
+    }
+}
+
+void DisplayDevice::setFrame(const Rect& frame) {
+    if (frame.isValid()) {
+        mFrame = frame;
+        updateGeometryTransform();
+    }
+}
+
+void DisplayDevice::updateGeometryTransform() {
     int w = mDisplayWidth;
     int h = mDisplayHeight;
+    Transform R, S;
+    if (DisplayDevice::orientationToTransfrom(
+            mOrientation, w, h, &R) == NO_ERROR) {
+        dirtyRegion.set(bounds());
 
-    DisplayDevice::orientationToTransfrom(
-            orientation, w, h, &mGlobalTransform);
-    if (orientation & DisplayState::eOrientationSwapMask) {
-        int tmp = w;
-        w = h;
-        h = tmp;
+        Rect viewport(mViewport);
+        Rect frame(mFrame);
+
+        if (!frame.isValid()) {
+            // the destination frame can be invalid if it has never been set,
+            // in that case we assume the whole display frame.
+            frame = Rect(w, h);
+        }
+
+        if (viewport.isEmpty()) {
+            // viewport can be invalid if it has never been set, in that case
+            // we assume the whole display size.
+            // it's also invalid to have an empty viewport, so we handle that
+            // case in the same way.
+            viewport = Rect(w, h);
+            if (R.getOrientation() & Transform::ROT_90) {
+                // viewport is always specified in the logical orientation
+                // of the display (ie: post-rotation).
+                swap(viewport.right, viewport.bottom);
+            }
+        }
+
+        float src_width  = viewport.width();
+        float src_height = viewport.height();
+        float dst_width  = frame.width();
+        float dst_height = frame.height();
+        if (src_width != src_height || dst_width != dst_height) {
+            float sx = dst_width  / src_width;
+            float sy = dst_height / src_height;
+            S.set(sx, 0, 0, sy);
+        }
+        float src_x = viewport.left;
+        float src_y = viewport.top;
+        float dst_x = frame.left;
+        float dst_y = frame.top;
+        float tx = dst_x - src_x;
+        float ty = dst_y - src_y;
+        S.set(tx, ty);
+
+        // rotate first, followed by scaling
+        mGlobalTransform = S * R;
     }
-    mOrientation = orientation;
-    dirtyRegion.set(bounds());
-    return NO_ERROR;
 }
diff --git a/services/surfaceflinger/DisplayDevice.h b/services/surfaceflinger/DisplayDevice.h
index b742b13..12a0152 100644
--- a/services/surfaceflinger/DisplayDevice.h
+++ b/services/surfaceflinger/DisplayDevice.h
@@ -93,11 +93,16 @@
     bool                    getSecureLayerVisible() const;
     Region                  getDirtyRegion(bool repaintEverything) const;
 
-    status_t                setOrientation(int orientation);
     void                    setLayerStack(uint32_t stack);
+    void                    setOrientation(int orientation);
+    void                    setViewport(const Rect& viewport);
+    void                    setFrame(const Rect& frame);
 
     int                     getOrientation() const { return mOrientation; }
     const Transform&        getTransform() const { return mGlobalTransform; }
+    const Rect&             getViewport() const { return mViewport; }
+    const Rect&             getFrame() const { return mFrame; }
+
     uint32_t                getLayerStack() const { return mLayerStack; }
     int32_t                 getDisplayType() const { return mType; }
     int32_t                 getHwcDisplayId() const { return mHwcDisplayId; }
@@ -110,7 +115,8 @@
     }
     inline Rect bounds() const { return getBounds(); }
 
-    static void makeCurrent(const sp<const DisplayDevice>& hw, EGLContext ctx);
+    static EGLBoolean makeCurrent(EGLDisplay dpy,
+            const sp<const DisplayDevice>& hw, EGLContext ctx);
 
     /* ------------------------------------------------------------------------
      * blank / unplank management
@@ -170,11 +176,16 @@
     /*
      * Transaction state
      */
-    static status_t orientationToTransfrom(int orientation, int w, int h,
-            Transform* tr);
-    Transform mGlobalTransform;
-    int mOrientation;
+    static status_t orientationToTransfrom(int orientation,
+            int w, int h, Transform* tr);
+
+    void updateGeometryTransform();
+
     uint32_t mLayerStack;
+    int mOrientation;
+    Rect mViewport;
+    Rect mFrame;
+    Transform mGlobalTransform;
 };
 
 }; // namespace android
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index ee653f3..ed91d44 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -297,8 +297,8 @@
     return ctxt;
 }
 
-void SurfaceFlinger::initializeGL(EGLDisplay display, EGLSurface surface) {
-    EGLBoolean result = eglMakeCurrent(display, surface, surface, mEGLContext);
+void SurfaceFlinger::initializeGL(EGLDisplay display, const sp<DisplayDevice>& hw) {
+    EGLBoolean result = DisplayDevice::makeCurrent(display, hw, mEGLContext);
     if (!result) {
         ALOGE("Couldn't create a working GLES context. check logs. exiting...");
         exit(0);
@@ -314,10 +314,6 @@
             eglQueryString(display, EGL_VERSION),
             eglQueryString(display, EGL_EXTENSIONS));
 
-    EGLint w, h;
-    eglQuerySurface(display, surface, EGL_WIDTH,  &w);
-    eglQuerySurface(display, surface, EGL_HEIGHT, &h);
-
     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
     glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);
 
@@ -344,12 +340,6 @@
     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0,
             GL_RGB, GL_UNSIGNED_SHORT_5_6_5, protTexData);
 
-    glViewport(0, 0, w, h);
-    glMatrixMode(GL_PROJECTION);
-    glLoadIdentity();
-    // put the origin in the left-bottom corner
-    glOrthof(0, w, 0, h, 0, 1); // l=0, r=w ; b=0, t=h
-
     // print some debugging info
     EGLint r,g,b,a;
     eglGetConfigAttrib(display, mEGLConfig, EGL_RED_SIZE,   &r);
@@ -412,8 +402,7 @@
     mDisplays.add(mDefaultDisplays[DisplayDevice::DISPLAY_PRIMARY], hw);
 
     //  initialize OpenGL ES
-    EGLSurface surface = hw->getEGLSurface();
-    initializeGL(mEGLDisplay, surface);
+    initializeGL(mEGLDisplay, hw);
 
     // start the EventThread
     mEventThread = new EventThread(this);
@@ -863,7 +852,7 @@
         // FIXME: EGL spec says:
         //   "surface must be bound to the calling thread's current context,
         //    for the current rendering API."
-        DisplayDevice::makeCurrent(getDefaultDisplayDevice(), mEGLContext);
+        DisplayDevice::makeCurrent(mEGLDisplay, getDefaultDisplayDevice(), mEGLContext);
         hwc.commit();
     }
 
@@ -983,11 +972,14 @@
                         if (state.layerStack != draw[i].layerStack) {
                             disp->setLayerStack(state.layerStack);
                         }
-                        if (state.orientation != draw[i].orientation ||
-                                state.viewport != draw[i].viewport ||
-                                state.frame != draw[i].frame) {
+                        if (state.orientation != draw[i].orientation) {
                             disp->setOrientation(state.orientation);
-                            // TODO: take viewport and frame into account
+                        }
+                        if (state.viewport != draw[i].viewport) {
+                            disp->setViewport(state.viewport);
+                        }
+                        if (state.frame != draw[i].frame) {
+                            disp->setFrame(state.frame);
                         }
                     }
                 }
@@ -1006,7 +998,8 @@
                                 state.type, display, stc, 0, mEGLConfig);
                         disp->setLayerStack(state.layerStack);
                         disp->setOrientation(state.orientation);
-                        // TODO: take viewport and frame into account
+                        disp->setViewport(state.viewport);
+                        disp->setFrame(state.frame);
                         mDisplays.add(display, disp);
                     }
                 }
@@ -1280,7 +1273,7 @@
 
     const bool hasGlesComposition = hwc.hasGlesComposition(id) || (cur==end);
     if (hasGlesComposition) {
-        DisplayDevice::makeCurrent(hw, mEGLContext);
+        DisplayDevice::makeCurrent(mEGLDisplay, hw, mEGLContext);
 
         // set the frame buffer
         glMatrixMode(GL_MODELVIEW);
@@ -1977,15 +1970,18 @@
         const sp<const DisplayDevice>& hw(mDisplays[dpy]);
         snprintf(buffer, SIZE,
                 "+ DisplayDevice[%u]\n"
-                "   id=%x, layerStack=%u, (%4dx%4d), orient=%2d, tr=%08x, "
-                "flips=%u, secure=%d, numLayers=%u\n",
+                "   id=%x, layerStack=%u, (%4dx%4d), orient=%2d (type=%08x), "
+                "flips=%u, secure=%d, numLayers=%u, v:[%d,%d,%d,%d], f:[%d,%d,%d,%d]\n",
                 dpy,
                 hw->getDisplayType(), hw->getLayerStack(),
                 hw->getWidth(), hw->getHeight(),
                 hw->getOrientation(), hw->getTransform().getType(),
                 hw->getPageFlipCount(),
                 hw->getSecureLayerVisible(),
-                hw->getVisibleLayersSortedByZ().size());
+                hw->getVisibleLayersSortedByZ().size(),
+                hw->getViewport().left, hw->getViewport().top, hw->getViewport().right, hw->getViewport().bottom,
+                hw->getFrame().left, hw->getFrame().top, hw->getFrame().right, hw->getFrame().bottom);
+
         result.append(buffer);
     }
 
@@ -2488,6 +2484,8 @@
 
 SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type)
     : type(type), layerStack(0), orientation(0) {
+    viewport.makeInvalid();
+    frame.makeInvalid();
 }
 
 // ---------------------------------------------------------------------------
diff --git a/services/surfaceflinger/SurfaceFlinger.h b/services/surfaceflinger/SurfaceFlinger.h
index 9db6b2d..ea03e2d 100644
--- a/services/surfaceflinger/SurfaceFlinger.h
+++ b/services/surfaceflinger/SurfaceFlinger.h
@@ -320,7 +320,7 @@
         EGLint const* attrs, PixelFormat format, EGLConfig* outConfig);
     static EGLConfig selectEGLConfig(EGLDisplay disp, EGLint visualId);
     static EGLContext createGLContext(EGLDisplay disp, EGLConfig config);
-    void initializeGL(EGLDisplay display, EGLSurface surface);
+    void initializeGL(EGLDisplay display, const sp<DisplayDevice>& hw);
     uint32_t getMaxTextureSize() const;
     uint32_t getMaxViewportDims() const;