From b661d66013b9803c50dc78ca0247ac39caef443a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 19 Aug 2010 17:01:19 -0700 Subject: [PATCH] fix [2931513] Add support for setting the orientation of an ANativeWindow Also implement support for cropping. Change-Id: Iba5888dd242bf2feaac9e9ce26e404c1f404c280 --- .../surfaceflinger/SharedBufferStack.h | 18 +-- include/surfaceflinger/Surface.h | 3 + include/ui/egl/android_natives.h | 29 +++++ .../SharedBufferStack.cpp | 46 ++++++++ libs/surfaceflinger_client/Surface.cpp | 22 ++++ services/surfaceflinger/Layer.cpp | 9 +- services/surfaceflinger/LayerBase.cpp | 107 ++++++++++++++---- services/surfaceflinger/LayerBase.h | 8 ++ services/surfaceflinger/LayerBlur.cpp | 2 + services/surfaceflinger/LayerBuffer.cpp | 2 +- services/surfaceflinger/TextureManager.h | 3 +- 11 files changed, 212 insertions(+), 37 deletions(-) diff --git a/include/private/surfaceflinger/SharedBufferStack.h b/include/private/surfaceflinger/SharedBufferStack.h index 1eb178ebc..d016dfa96 100644 --- a/include/private/surfaceflinger/SharedBufferStack.h +++ b/include/private/surfaceflinger/SharedBufferStack.h @@ -54,11 +54,6 @@ class SharedClient; // ---------------------------------------------------------------------------- -// 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 @@ public: }; 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 @@ public: 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 @@ public: 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 @@ public: 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 @@ public: 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 294c867c3..7c5a39b68 100644 --- a/include/surfaceflinger/Surface.h +++ b/include/surfaceflinger/Surface.h @@ -216,6 +216,7 @@ private: 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 @@ private: 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 @@ private: 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 ca89b0681..d59d72b7e 100644 --- a/include/ui/egl/android_natives.h +++ b/include/ui/egl/android_natives.h @@ -85,6 +85,7 @@ enum { 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 @@ enum { 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 @@ struct ANativeWindow * NATIVE_WINDOW_SET_CROP * NATIVE_WINDOW_SET_BUFFER_COUNT * NATIVE_WINDOW_SET_BUFFERS_GEOMETRY + * NATIVE_WINDOW_SET_BUFFERS_TRANSFORM * */ @@ -298,6 +314,19 @@ static inline int native_window_set_buffers_geometry( 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 156a7db3c..4ad9f861c 100644 --- a/libs/surfaceflinger_client/SharedBufferStack.cpp +++ b/libs/surfaceflinger_client/SharedBufferStack.cpp @@ -75,6 +75,14 @@ status_t SharedBufferStack::setCrop(int buffer, const Rect& crop) 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 @@ Region SharedBufferStack::getDirtyRegion(int buffer) const 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 @@ status_t SharedBufferClient::setCrop(int buf, const Rect& crop) 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 @@ Region SharedBufferServer::getDirtyRegion(int buf) const 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 5ab72cd37..cb7609176 100644 --- a/libs/surfaceflinger_client/Surface.cpp +++ b/libs/surfaceflinger_client/Surface.cpp @@ -422,8 +422,10 @@ void Surface::init() const_cast(ANativeWindow::maxSwapInterval) = 1; const_cast(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 @@ int Surface::queueBuffer(android_native_buffer_t* buffer) } 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 @@ int Surface::perform(int operation, va_list args) 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 @@ int Surface::dispatch_set_buffers_geometry(va_list args) { 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::disconnect(int api) 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 @@ int Surface::setBuffersGeometry(int w, int h, int format) 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 629d993ca..6f3c66d74 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -473,9 +473,9 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) return; } - // get the dirty region sp 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 @@ void Layer::lockPageFlip(bool& recomputeVisibleRegions) // 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 91ac915b7..6fc50109e 100644 --- a/services/surfaceflinger/LayerBase.cpp +++ b/services/surfaceflinger/LayerBase.cpp @@ -54,6 +54,8 @@ LayerBase::LayerBase(SurfaceFlinger* flinger, DisplayID display) { const DisplayHardware& hw(flinger->graphicPlane(0).displayHardware()); mFlags = hw.getFlags(); + mBufferCrop.makeInvalid(); + mBufferTransform = 0; } LayerBase::~LayerBase() @@ -345,6 +347,14 @@ void LayerBase::clearWithOpenGL(const Region& clip) const clearWithOpenGL(clip,0,0,0,0); } +template +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 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const } } - 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 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const 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 @@ void LayerBase::drawWithOpenGL(const Region& clip, const Texture& texture) const 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 22bf85706..8cba287f8 100644 --- a/services/surfaceflinger/LayerBase.h +++ b/services/surfaceflinger/LayerBase.h @@ -226,9 +226,17 @@ protected: 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 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 64a43c78f..2ee21b92b 100644 --- a/services/surfaceflinger/LayerBlur.cpp +++ b/services/surfaceflinger/LayerBlur.cpp @@ -241,6 +241,8 @@ void LayerBlur::onDraw(const Region& clip) const 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 5f836366c..0240748ce 100644 --- a/services/surfaceflinger/LayerBuffer.cpp +++ b/services/surfaceflinger/LayerBuffer.cpp @@ -485,7 +485,7 @@ void LayerBuffer::BufferSource::onDraw(const Region& clip) const 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 c7c14e70b..18c434869 100644 --- a/services/surfaceflinger/TextureManager.h +++ b/services/surfaceflinger/TextureManager.h @@ -40,12 +40,11 @@ class GraphicBuffer; 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; };