SurfaceTexture: improve texture matrix computation

This change reduces the shrinking of the crop rectangle in some cases.  It adds
a way to inform the SurfaceTexture that its texture will be used without
bilinear interpolation, and uses knowledge of the pixel format to avoid
shrinking unecessarily.

Change-Id: I72365f39f74ecb7fcc51b4cf42f2d0fa97727212
This commit is contained in:
Jamie Gennis 2012-05-08 16:56:34 -07:00
parent d72f233ffa
commit 5c1139fea3
2 changed files with 59 additions and 30 deletions

View File

@ -144,6 +144,10 @@ public:
// updateTexImage() is called. // updateTexImage() is called.
status_t setDefaultBufferSize(uint32_t width, uint32_t height); status_t setDefaultBufferSize(uint32_t width, uint32_t height);
// setFilteringEnabled sets whether the transform matrix should be computed
// for use with bilinear filtering.
void setFilteringEnabled(bool enabled);
// getCurrentBuffer returns the buffer associated with the current image. // getCurrentBuffer returns the buffer associated with the current image.
sp<GraphicBuffer> getCurrentBuffer() const; sp<GraphicBuffer> getCurrentBuffer() const;
@ -289,6 +293,11 @@ private:
uint32_t mDefaultWidth, mDefaultHeight; uint32_t mDefaultWidth, mDefaultHeight;
// mFilteringEnabled indicates whether the transform matrix is computed for
// use with bilinear filtering. It defaults to true and is changed by
// setFilteringEnabled().
bool mFilteringEnabled;
// mTexName is the name of the OpenGL texture to which streamed images will // mTexName is the name of the OpenGL texture to which streamed images will
// be bound when updateTexImage is called. It is set at construction time // be bound when updateTexImage is called. It is set at construction time
// and can be changed with a call to attachToContext. // and can be changed with a call to attachToContext.

View File

@ -108,6 +108,7 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode,
GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) : GLenum texTarget, bool useFenceSync, const sp<BufferQueue> &bufferQueue) :
mCurrentTransform(0), mCurrentTransform(0),
mCurrentTimestamp(0), mCurrentTimestamp(0),
mFilteringEnabled(true),
mTexName(tex), mTexName(tex),
#ifdef USE_FENCE_SYNC #ifdef USE_FENCE_SYNC
mUseFenceSync(useFenceSync), mUseFenceSync(useFenceSync),
@ -503,6 +504,15 @@ void SurfaceTexture::getTransformMatrix(float mtx[16]) {
memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix)); memcpy(mtx, mCurrentTransformMatrix, sizeof(mCurrentTransformMatrix));
} }
void SurfaceTexture::setFilteringEnabled(bool enabled) {
Mutex::Autolock lock(mMutex);
bool needsRecompute = mFilteringEnabled != enabled;
mFilteringEnabled = enabled;
if (needsRecompute) {
computeCurrentTransformMatrix();
}
}
void SurfaceTexture::computeCurrentTransformMatrix() { void SurfaceTexture::computeCurrentTransformMatrix() {
ST_LOGV("computeCurrentTransformMatrix"); ST_LOGV("computeCurrentTransformMatrix");
@ -534,39 +544,49 @@ void SurfaceTexture::computeCurrentTransformMatrix() {
sp<GraphicBuffer>& buf(mCurrentTextureBuf); sp<GraphicBuffer>& buf(mCurrentTextureBuf);
Rect cropRect = mCurrentCrop; Rect cropRect = mCurrentCrop;
float tx, ty, sx, sy; float tx = 0.0f, ty = 0.0f, sx = 1.0f, sy = 1.0f;
float bufferWidth = buf->getWidth();
float bufferHeight = buf->getHeight();
if (!cropRect.isEmpty()) { if (!cropRect.isEmpty()) {
// In order to prevent bilinear sampling at the of the crop rectangle we float shrinkAmount = 0.0f;
// may need to shrink it by 2 texels in each direction. Normally this if (mFilteringEnabled) {
// would just need to take 1/2 a texel off each end, but because the // In order to prevent bilinear sampling beyond the edge of the
// chroma channels will likely be subsampled we need to chop off a whole // crop rectangle we may need to shrink it by 2 texels in each
// texel. This will cause artifacts if someone does nearest sampling // dimension. Normally this would just need to take 1/2 a texel
// with 1:1 pixel:texel ratio, but it's impossible to simultaneously // off each end, but because the chroma channels of YUV420 images
// accomodate the bilinear and nearest sampling uses. // are subsampled we may need to shrink the crop region by a whole
// // texel on each side.
// If nearest sampling turns out to be a desirable usage of these switch (buf->getPixelFormat()) {
// textures then we could add the ability to switch a SurfaceTexture to case PIXEL_FORMAT_RGBA_8888:
// nearest-mode. Preferably, however, the image producers (video case PIXEL_FORMAT_RGBX_8888:
// decoder, camera, etc.) would simply not use a crop rectangle (or at case PIXEL_FORMAT_RGB_888:
// least not tell the framework about it) so that the GPU can do the case PIXEL_FORMAT_RGB_565:
// correct edge behavior. case PIXEL_FORMAT_BGRA_8888:
const float shrinkAmount = 1.0f; // the amount that each edge is shrunk case PIXEL_FORMAT_RGBA_5551:
case PIXEL_FORMAT_RGBA_4444:
// We know there's no subsampling of any channels, so we
// only need to shrink by a half a pixel.
shrinkAmount = 0.5;
float bufferWidth = buf->getWidth(); default:
float bufferHeight = buf->getHeight(); // If we don't recognize the format, we must assume the
// worst case (that we care about), which is YUV420.
shrinkAmount = 1.0;
}
}
tx = (float(cropRect.left) + shrinkAmount) / bufferWidth; // Only shrink the dimensions that are not the size of the buffer.
ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) / if (cropRect.width() < bufferWidth) {
bufferHeight; tx = (float(cropRect.left) + shrinkAmount) / bufferWidth;
sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) / sx = (float(cropRect.width()) - (2.0f * shrinkAmount)) /
bufferWidth; bufferWidth;
sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) / }
bufferHeight; if (cropRect.height() < bufferHeight) {
} else { ty = (float(bufferHeight - cropRect.bottom) + shrinkAmount) /
tx = 0.0f; bufferHeight;
ty = 0.0f; sy = (float(cropRect.height()) - (2.0f * shrinkAmount)) /
sx = 1.0f; bufferHeight;
sy = 1.0f; }
} }
float crop[16] = { float crop[16] = {
sx, 0, 0, 0, sx, 0, 0, 0,