fix [2931513] Add support for setting the orientation of an ANativeWindow

Also implement support for cropping.

Change-Id: Iba5888dd242bf2feaac9e9ce26e404c1f404c280
diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h
index 1eb178e..d016dfa 100644
--- a/include/private/surfaceflinger/SharedBufferStack.h
+++ b/include/private/surfaceflinger/SharedBufferStack.h
@@ -54,11 +54,6 @@
 
 // ----------------------------------------------------------------------------
 
-// 4 * (11 + 7 + (1 + 2*NUM_RECT_MAX) * NUM_BUFFER_MAX) * NUM_LAYERS_MAX
-// 4 * (11 + 7 + (1 + 2*7)*16) * 31
-// 1032 * 31
-// = ~27 KiB (31992)
-
 class SharedBufferStack
 {
     friend class SharedClient;
@@ -85,7 +80,7 @@
     };
 
     struct FlatRegion { // 52 bytes = 4 * (1 + 2*N)
-        static const unsigned int NUM_RECT_MAX = 6;
+        static const unsigned int NUM_RECT_MAX = 5;
         uint32_t    count;
         SmallRect   rects[NUM_RECT_MAX];
     };
@@ -93,13 +88,18 @@
     struct BufferData {
         FlatRegion dirtyRegion;
         SmallRect  crop;
+        uint8_t transform;
+        uint8_t reserved[3];
     };
     
     SharedBufferStack();
     void init(int32_t identity);
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
+    status_t setTransform(int buffer, uint8_t transform);
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     // these attributes are part of the conditions/updates
     volatile int32_t head;      // server's current front buffer
@@ -117,7 +117,7 @@
     int32_t     reserved32[1];
     Statistics  stats;
     int32_t     reserved;
-    BufferData  buffers[NUM_BUFFER_MAX];     // 960 bytes
+    BufferData  buffers[NUM_BUFFER_MAX];     // 1024 bytes
 };
 
 // ----------------------------------------------------------------------------
@@ -206,7 +206,7 @@
     bool needNewBuffer(int buffer) const;
     status_t setDirtyRegion(int buffer, const Region& reg);
     status_t setCrop(int buffer, const Rect& reg);
-    
+    status_t setTransform(int buffer, uint32_t transform);
 
     class SetBufferCountCallback {
         friend class SharedBufferClient;
@@ -275,6 +275,8 @@
     status_t reallocateAllExcept(int buffer);
     int32_t getQueuedCount() const;
     Region getDirtyRegion(int buffer) const;
+    Rect getCrop(int buffer) const;
+    uint32_t getTransform(int buffer) const;
 
     status_t resize(int newNumBuffers);
 
diff --git a/include/surfaceflinger/Surface.h b/include/surfaceflinger/Surface.h
index 294c867..7c5a39b 100644
--- a/include/surfaceflinger/Surface.h
+++ b/include/surfaceflinger/Surface.h
@@ -216,6 +216,7 @@
     int  dispatch_crop(va_list args);
     int  dispatch_set_buffer_count(va_list args);
     int  dispatch_set_buffers_geometry(va_list args);
+    int  dispatch_set_buffers_transform(va_list args);
     
     void setUsage(uint32_t reqUsage);
     int  connect(int api);
@@ -223,6 +224,7 @@
     int  crop(Rect const* rect);
     int  setBufferCount(int bufferCount);
     int  setBuffersGeometry(int w, int h, int format);
+    int  setBuffersTransform(int transform);
 
     /*
      *  private stuff...
@@ -278,6 +280,7 @@
     Rect                        mSwapRectangle;
     int                         mConnected;
     Rect                        mNextBufferCrop;
+    uint32_t                    mNextBufferTransform;
     BufferInfo                  mBufferInfo;
     
     // protected by mSurfaceLock. These are also used from lock/unlock
diff --git a/include/ui/egl/android_natives.h b/include/ui/egl/android_natives.h
index ca89b06..d59d72b 100644
--- a/include/ui/egl/android_natives.h
+++ b/include/ui/egl/android_natives.h
@@ -85,6 +85,7 @@
     NATIVE_WINDOW_SET_CROP,
     NATIVE_WINDOW_SET_BUFFER_COUNT,
     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY,
+    NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
 };
 
 /* parameter for NATIVE_WINDOW_[DIS]CONNECT */
@@ -92,6 +93,20 @@
     NATIVE_WINDOW_API_EGL = 1
 };
 
+/* parameter for NATIVE_WINDOW_SET_BUFFERS_TRANSFORM */
+enum {
+    /* flip source image horizontally */
+    NATIVE_WINDOW_TRANSFORM_FLIP_H = HAL_TRANSFORM_FLIP_H ,
+    /* flip source image vertically */
+    NATIVE_WINDOW_TRANSFORM_FLIP_V = HAL_TRANSFORM_FLIP_V,
+    /* rotate source image 90 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_90 = HAL_TRANSFORM_ROT_90,
+    /* rotate source image 180 degrees */
+    NATIVE_WINDOW_TRANSFORM_ROT_180 = HAL_TRANSFORM_ROT_180,
+    /* rotate source image 270 degrees clock-wise */
+    NATIVE_WINDOW_TRANSFORM_ROT_270 = HAL_TRANSFORM_ROT_270,
+};
+
 struct ANativeWindow 
 {
 #ifdef __cplusplus
@@ -196,6 +211,7 @@
      *     NATIVE_WINDOW_SET_CROP
      *     NATIVE_WINDOW_SET_BUFFER_COUNT
      *     NATIVE_WINDOW_SET_BUFFERS_GEOMETRY
+     *     NATIVE_WINDOW_SET_BUFFERS_TRANSFORM
      *  
      */
     
@@ -298,6 +314,19 @@
             w, h, format);
 }
 
+/*
+ * native_window_set_buffers_transform(..., int transform)
+ * All buffers queued after this call will be displayed transformed according
+ * to the transform parameter specified.
+ */
+static inline int native_window_set_buffers_transform(
+        ANativeWindow* window,
+        int transform)
+{
+    return window->perform(window, NATIVE_WINDOW_SET_BUFFERS_TRANSFORM,
+            transform);
+}
+
 // ---------------------------------------------------------------------------
 
 /* FIXME: this is legacy for pixmaps */
diff --git a/libs/surfaceflinger_client/SharedBufferStack.cpp b/libs/surfaceflinger_client/SharedBufferStack.cpp
index 156a7db..4ad9f86 100644
--- a/libs/surfaceflinger_client/SharedBufferStack.cpp
+++ b/libs/surfaceflinger_client/SharedBufferStack.cpp
@@ -75,6 +75,14 @@
     return NO_ERROR;
 }
 
+status_t SharedBufferStack::setTransform(int buffer, uint8_t transform)
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return BAD_INDEX;
+    buffers[buffer].transform = transform;
+    return NO_ERROR;
+}
+
 status_t SharedBufferStack::setDirtyRegion(int buffer, const Region& dirty)
 {
     if (uint32_t(buffer) >= NUM_BUFFER_MAX)
@@ -137,6 +145,26 @@
     return res;
 }
 
+Rect SharedBufferStack::getCrop(int buffer) const
+{
+    Rect res(-1, -1);
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return res;
+    res.left = buffers[buffer].crop.l;
+    res.top = buffers[buffer].crop.t;
+    res.right = buffers[buffer].crop.r;
+    res.bottom = buffers[buffer].crop.b;
+    return res;
+}
+
+uint32_t SharedBufferStack::getTransform(int buffer) const
+{
+    if (uint32_t(buffer) >= NUM_BUFFER_MAX)
+        return 0;
+    return buffers[buffer].transform;
+}
+
+
 // ----------------------------------------------------------------------------
 
 SharedBufferBase::SharedBufferBase(SharedClient* sharedClient,
@@ -433,6 +461,12 @@
     return stack.setCrop(buf, crop);
 }
 
+status_t SharedBufferClient::setTransform(int buf, uint32_t transform)
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.setTransform(buf, uint8_t(transform));
+}
+
 status_t SharedBufferClient::setDirtyRegion(int buf, const Region& reg)
 {
     SharedBufferStack& stack( *mSharedStack );
@@ -549,6 +583,18 @@
     return stack.getDirtyRegion(buf);
 }
 
+Rect SharedBufferServer::getCrop(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getCrop(buf);
+}
+
+uint32_t SharedBufferServer::getTransform(int buf) const
+{
+    SharedBufferStack& stack( *mSharedStack );
+    return stack.getTransform(buf);
+}
+
 /*
  * NOTE: this is not thread-safe on the server-side, meaning
  * 'head' cannot move during this operation. The client-side
diff --git a/libs/surfaceflinger_client/Surface.cpp b/libs/surfaceflinger_client/Surface.cpp
index 5ab72cd..cb76091 100644
--- a/libs/surfaceflinger_client/Surface.cpp
+++ b/libs/surfaceflinger_client/Surface.cpp
@@ -422,8 +422,10 @@
     const_cast<int&>(ANativeWindow::maxSwapInterval) = 1;
     const_cast<uint32_t&>(ANativeWindow::flags) = 0;
 
+    mNextBufferTransform = 0;
     mConnected = 0;
     mSwapRectangle.makeInvalid();
+    mNextBufferCrop = Rect(0,0);
     // two buffers by default
     mBuffers.setCapacity(2);
     mBuffers.insertAt(0, 2);
@@ -631,6 +633,7 @@
     }
     
     int32_t bufIdx = getBufferIndex(GraphicBuffer::getSelf(buffer));
+    mSharedBufferClient->setTransform(bufIdx, mNextBufferTransform);
     mSharedBufferClient->setCrop(bufIdx, mNextBufferCrop);
     mSharedBufferClient->setDirtyRegion(bufIdx, mDirtyRegion);
     err = mSharedBufferClient->queue(bufIdx);
@@ -685,6 +688,9 @@
     case NATIVE_WINDOW_SET_BUFFERS_GEOMETRY:
         res = dispatch_set_buffers_geometry( args );
         break;
+    case NATIVE_WINDOW_SET_BUFFERS_TRANSFORM:
+        res = dispatch_set_buffers_transform( args );
+        break;
     default:
         res = NAME_NOT_FOUND;
         break;
@@ -719,6 +725,11 @@
     return setBuffersGeometry(w, h, f);
 }
 
+int Surface::dispatch_set_buffers_transform(va_list args) {
+    int transform = va_arg(args, int);
+    return setBuffersTransform(transform);
+}
+
 void Surface::setUsage(uint32_t reqUsage)
 {
     Mutex::Autolock _l(mSurfaceLock);
@@ -765,6 +776,10 @@
 
 int Surface::crop(Rect const* rect)
 {
+    // empty/invalid rects are not allowed
+    if (rect->isEmpty())
+        return BAD_VALUE;
+
     Mutex::Autolock _l(mSurfaceLock);
     // TODO: validate rect size
     mNextBufferCrop = *rect;
@@ -804,6 +819,13 @@
     return NO_ERROR;
 }
 
+int Surface::setBuffersTransform(int transform)
+{
+    Mutex::Autolock _l(mSurfaceLock);
+    mNextBufferTransform = transform;
+    return NO_ERROR;
+}
+
 // ----------------------------------------------------------------------------
 
 int Surface::getConnectedApi() const
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index 629d993..6f3c66d 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -473,9 +473,9 @@
         return;
     }
 
-    // get the dirty region
     sp<GraphicBuffer> newFrontBuffer(getBuffer(buf));
     if (newFrontBuffer != NULL) {
+        // get the dirty region
         // compute the posted region
         const Region dirty(lcblk->getDirtyRegion(buf));
         mPostedDirtyRegion = dirty.intersect( newFrontBuffer->getBounds() );
@@ -511,6 +511,13 @@
             // we now have the correct size, unfreeze the screen
             mFreezeLock.clear();
         }
+
+        // get the crop region
+        setBufferCrop( lcblk->getCrop(buf) );
+
+        // get the transformation
+        setBufferTransform( lcblk->getTransform(buf) );
+
     } else {
         // this should not happen unless we ran out of memory while
         // allocating the buffer. we're hoping that things will get back
diff --git a/services/surfaceflinger/LayerBase.cpp b/services/surfaceflinger/LayerBase.cpp
index 91ac915..6fc5010 100644
--- a/services/surfaceflinger/LayerBase.cpp
+++ b/services/surfaceflinger/LayerBase.cpp
@@ -54,6 +54,8 @@
 {
     const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware());
     mFlags = hw.getFlags();
+    mBufferCrop.makeInvalid();
+    mBufferTransform = 0;
 }
 
 LayerBase::~LayerBase()
@@ -345,6 +347,14 @@
     clearWithOpenGL(clip,0,0,0,0);
 }
 
+template <typename T>
+static inline
+void swap(T& a, T& b) {
+    T t(a);
+    a = b;
+    b = t;
+}
+
 void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const
 {
     const DisplayHardware& hw(graphicPlane(0).displayHardware());
@@ -378,37 +388,72 @@
         }
     }
 
-    Region::const_iterator it = clip.begin();
-    Region::const_iterator const end = clip.end();
-    const GLfloat texCoords[4][2] = {
-            { 0,  0 },
-            { 0,  1 },
-            { 1,  1 },
-            { 1,  0 }
+    /*
+     *  compute texture coordinates
+     *  here, we handle NPOT, cropping and buffer transformations
+     */
+
+    GLfloat cl, ct, cr, cb;
+    if (!mBufferCrop.isEmpty()) {
+        // source is cropped
+        const GLfloat us = (texture.NPOTAdjust ? texture.wScale : 1.0f) / width;
+        const GLfloat vs = (texture.NPOTAdjust ? texture.hScale : 1.0f) / height;
+        cl = mBufferCrop.left   * us;
+        ct = mBufferCrop.top    * vs;
+        cr = mBufferCrop.right  * us;
+        cb = mBufferCrop.bottom * vs;
+    } else {
+        cl = 0;
+        ct = 0;
+        cr = (texture.NPOTAdjust ? texture.wScale : 1.0f);
+        cb = (texture.NPOTAdjust ? texture.hScale : 1.0f);
+    }
+
+    struct TexCoords {
+        GLfloat u;
+        GLfloat v;
     };
 
-    glMatrixMode(GL_TEXTURE);
-    glLoadIdentity();
+    enum {
+        // name of the corners in the texture map
+        LB = 0, // left-bottom
+        LT = 1, // left-top
+        RT = 2, // right-top
+        RB = 3  // right-bottom
+    };
+
+    // vertices in screen space
+    int vLT = LB;
+    int vLB = LT;
+    int vRB = RT;
+    int vRT = RB;
 
     // the texture's source is rotated
-    switch (texture.transform) {
-        case HAL_TRANSFORM_ROT_90:
-            glTranslatef(0, 1, 0);
-            glRotatef(-90, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_180:
-            glTranslatef(1, 1, 0);
-            glRotatef(-180, 0, 0, 1);
-            break;
-        case HAL_TRANSFORM_ROT_270:
-            glTranslatef(1, 0, 0);
-            glRotatef(-270, 0, 0, 1);
-            break;
+    uint32_t transform = mBufferTransform;
+    if (transform & HAL_TRANSFORM_ROT_90) {
+        vLT = RB;
+        vLB = LB;
+        vRB = LT;
+        vRT = RT;
+    }
+    if (transform & HAL_TRANSFORM_FLIP_V) {
+        swap(vLT, vLB);
+        swap(vRB, vRT);
+    }
+    if (transform & HAL_TRANSFORM_FLIP_H) {
+        swap(vLT, vRB);
+        swap(vLB, vRT);
     }
 
-    if (texture.NPOTAdjust) {
-        glScalef(texture.wScale, texture.hScale, 1.0f);
-    }
+    TexCoords texCoords[4];
+    texCoords[vLT].u = cl;
+    texCoords[vLT].v = ct;
+    texCoords[vLB].u = cl;
+    texCoords[vLB].v = cb;
+    texCoords[vRB].u = cr;
+    texCoords[vRB].v = cb;
+    texCoords[vRT].u = cr;
+    texCoords[vRT].v = ct;
 
     if (needsDithering()) {
         glEnable(GL_DITHER);
@@ -420,6 +465,8 @@
     glVertexPointer(2, GL_FLOAT, 0, mVertices);
     glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
 
+    Region::const_iterator it = clip.begin();
+    Region::const_iterator const end = clip.end();
     while (it != end) {
         const Rect& r = *it++;
         const GLint sy = fbHeight - (r.top + r.height());
@@ -429,6 +476,16 @@
     glDisableClientState(GL_TEXTURE_COORD_ARRAY);
 }
 
+void LayerBase::setBufferCrop(const Rect& crop) {
+    if (!crop.isEmpty()) {
+        mBufferCrop = crop;
+    }
+}
+
+void LayerBase::setBufferTransform(uint32_t transform) {
+    mBufferTransform = transform;
+}
+
 void LayerBase::dump(String8& result, char* buffer, size_t SIZE) const
 {
     const Layer::State& s(drawingState());
diff --git a/services/surfaceflinger/LayerBase.h b/services/surfaceflinger/LayerBase.h
index 22bf857..8cba287 100644
--- a/services/surfaceflinger/LayerBase.h
+++ b/services/surfaceflinger/LayerBase.h
@@ -226,9 +226,17 @@
           void clearWithOpenGL(const Region& clip) const;
           void drawWithOpenGL(const Region& clip, const Texture& texture) const;
           
+          // these must be called from the post/drawing thread
+          void setBufferCrop(const Rect& crop);
+          void setBufferTransform(uint32_t transform);
+
                 sp<SurfaceFlinger> mFlinger;
                 uint32_t        mFlags;
 
+                // post/drawing thread
+                Rect mBufferCrop;
+                uint32_t mBufferTransform;
+
                 // cached during validateVisibility()
                 bool            mNeedsFiltering;
                 int32_t         mOrientation;
diff --git a/services/surfaceflinger/LayerBlur.cpp b/services/surfaceflinger/LayerBlur.cpp
index 64a43c7..2ee21b9 100644
--- a/services/surfaceflinger/LayerBlur.cpp
+++ b/services/surfaceflinger/LayerBlur.cpp
@@ -241,6 +241,8 @@
             glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
         }
         glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+        glLoadIdentity();
+        glMatrixMode(GL_MODELVIEW);
     }
 }
 
diff --git a/services/surfaceflinger/LayerBuffer.cpp b/services/surfaceflinger/LayerBuffer.cpp
index 5f836366..0240748 100644
--- a/services/surfaceflinger/LayerBuffer.cpp
+++ b/services/surfaceflinger/LayerBuffer.cpp
@@ -485,7 +485,7 @@
         mTextureManager.loadTexture(&mTexture, dirty, t);
     }
 
-    mTexture.transform = mBufferHeap.transform;
+    mLayer.setBufferTransform(mBufferHeap.transform);
     mLayer.drawWithOpenGL(clip, mTexture);
 }
 
diff --git a/services/surfaceflinger/TextureManager.h b/services/surfaceflinger/TextureManager.h
index c7c14e7..18c43486 100644
--- a/services/surfaceflinger/TextureManager.h
+++ b/services/surfaceflinger/TextureManager.h
@@ -40,12 +40,11 @@
 struct Image {
     enum { TEXTURE_2D=0, TEXTURE_EXTERNAL=1 };
     Image() : name(-1U), image(EGL_NO_IMAGE_KHR), width(0), height(0),
-        transform(0), dirty(1), target(TEXTURE_2D) { }
+        dirty(1), target(TEXTURE_2D) { }
     GLuint        name;
     EGLImageKHR   image;
     GLuint        width;
     GLuint        height;
-    uint32_t      transform;
     unsigned      dirty     : 1;
     unsigned      target    : 1;
 };