2010-05-11 03:06:11 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2010 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
|
|
|
#include <utils/Errors.h>
|
|
|
|
#include <utils/Log.h>
|
|
|
|
|
|
|
|
#include <ui/GraphicBuffer.h>
|
|
|
|
|
|
|
|
#include <GLES/gl.h>
|
|
|
|
#include <GLES/glext.h>
|
|
|
|
|
|
|
|
#include <hardware/hardware.h>
|
|
|
|
|
|
|
|
#include "clz.h"
|
|
|
|
#include "DisplayHardware/DisplayHardware.h"
|
|
|
|
#include "TextureManager.h"
|
|
|
|
|
|
|
|
namespace android {
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
TextureManager::TextureManager(uint32_t flags)
|
|
|
|
: mFlags(flags)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
GLuint TextureManager::createTexture()
|
|
|
|
{
|
|
|
|
GLuint textureName = -1;
|
|
|
|
glGenTextures(1, &textureName);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, textureName);
|
|
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
return textureName;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TextureManager::isSupportedYuvFormat(int format)
|
|
|
|
{
|
|
|
|
switch (format) {
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_SP:
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_420_SP:
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_P:
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_420_P:
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_422_I:
|
|
|
|
case HAL_PIXEL_FORMAT_YCbCr_420_I:
|
|
|
|
case HAL_PIXEL_FORMAT_YCrCb_420_SP:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2010-05-19 00:06:55 +00:00
|
|
|
status_t TextureManager::initEglImage(Image* texture,
|
2010-05-11 03:06:11 +00:00
|
|
|
EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
|
|
|
|
{
|
|
|
|
status_t err = NO_ERROR;
|
|
|
|
if (!texture->dirty) return err;
|
|
|
|
|
|
|
|
// free the previous image
|
|
|
|
if (texture->image != EGL_NO_IMAGE_KHR) {
|
|
|
|
eglDestroyImageKHR(dpy, texture->image);
|
|
|
|
texture->image = EGL_NO_IMAGE_KHR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// construct an EGL_NATIVE_BUFFER_ANDROID
|
|
|
|
android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
|
|
|
|
|
|
|
|
// create the new EGLImageKHR
|
|
|
|
const EGLint attrs[] = {
|
|
|
|
EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
|
|
|
|
EGL_NONE, EGL_NONE
|
|
|
|
};
|
|
|
|
texture->image = eglCreateImageKHR(
|
|
|
|
dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
|
|
|
|
(EGLClientBuffer)clientBuf, attrs);
|
|
|
|
|
|
|
|
if (texture->image != EGL_NO_IMAGE_KHR) {
|
|
|
|
if (texture->name == -1UL) {
|
|
|
|
texture->name = createTexture();
|
|
|
|
texture->width = 0;
|
|
|
|
texture->height = 0;
|
|
|
|
}
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture->name);
|
|
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D,
|
|
|
|
(GLeglImageOES)texture->image);
|
|
|
|
GLint error = glGetError();
|
|
|
|
if (error != GL_NO_ERROR) {
|
|
|
|
LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
|
|
|
|
texture->image, error);
|
|
|
|
err = INVALID_OPERATION;
|
|
|
|
} else {
|
|
|
|
// Everything went okay!
|
|
|
|
texture->dirty = false;
|
|
|
|
texture->width = clientBuf->width;
|
|
|
|
texture->height = clientBuf->height;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
|
|
|
|
err = INVALID_OPERATION;
|
|
|
|
}
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
status_t TextureManager::loadTexture(Texture* texture,
|
|
|
|
const Region& dirty, const GGLSurface& t)
|
|
|
|
{
|
|
|
|
if (texture->name == -1UL) {
|
|
|
|
texture->name = createTexture();
|
|
|
|
texture->width = 0;
|
|
|
|
texture->height = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, texture->name);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In OpenGL ES we can't specify a stride with glTexImage2D (however,
|
|
|
|
* GL_UNPACK_ALIGNMENT is a limited form of stride).
|
|
|
|
* So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
|
|
|
|
* need to do something reasonable (here creating a bigger texture).
|
|
|
|
*
|
|
|
|
* extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
|
|
|
|
*
|
|
|
|
* This situation doesn't happen often, but some h/w have a limitation
|
|
|
|
* for their framebuffer (eg: must be multiple of 8 pixels), and
|
|
|
|
* we need to take that into account when using these buffers as
|
|
|
|
* textures.
|
|
|
|
*
|
|
|
|
* This should never be a problem with POT textures
|
|
|
|
*/
|
|
|
|
|
|
|
|
int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
|
|
|
|
unpack = 1 << ((unpack > 3) ? 3 : unpack);
|
|
|
|
glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* round to POT if needed
|
|
|
|
*/
|
|
|
|
if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
|
|
|
|
texture->NPOTAdjust = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (texture->NPOTAdjust) {
|
|
|
|
// find the smallest power-of-two that will accommodate our surface
|
|
|
|
texture->potWidth = 1 << (31 - clz(t.width));
|
|
|
|
texture->potHeight = 1 << (31 - clz(t.height));
|
|
|
|
if (texture->potWidth < t.width) texture->potWidth <<= 1;
|
|
|
|
if (texture->potHeight < t.height) texture->potHeight <<= 1;
|
|
|
|
texture->wScale = float(t.width) / texture->potWidth;
|
|
|
|
texture->hScale = float(t.height) / texture->potHeight;
|
|
|
|
} else {
|
|
|
|
texture->potWidth = t.width;
|
|
|
|
texture->potHeight = t.height;
|
|
|
|
}
|
|
|
|
|
|
|
|
Rect bounds(dirty.bounds());
|
|
|
|
GLvoid* data = 0;
|
|
|
|
if (texture->width != t.width || texture->height != t.height) {
|
|
|
|
texture->width = t.width;
|
|
|
|
texture->height = t.height;
|
|
|
|
|
|
|
|
// texture size changed, we need to create a new one
|
|
|
|
bounds.set(Rect(t.width, t.height));
|
|
|
|
if (t.width == texture->potWidth &&
|
|
|
|
t.height == texture->potHeight) {
|
|
|
|
// we can do it one pass
|
|
|
|
data = t.data;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
GL_RGB, texture->potWidth, texture->potHeight, 0,
|
|
|
|
GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
|
|
|
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
|
|
|
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
|
|
|
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
GL_RGBA, texture->potWidth, texture->potHeight, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, data);
|
|
|
|
} else if (isSupportedYuvFormat(t.format)) {
|
|
|
|
// just show the Y plane of YUV buffers
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
|
|
|
|
GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
|
|
|
|
} else {
|
|
|
|
// oops, we don't handle this format!
|
|
|
|
LOGE("texture=%d, using format %d, which is not "
|
|
|
|
"supported by the GL", texture->name, t.format);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!data) {
|
|
|
|
if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
0, bounds.top, t.width, bounds.height(),
|
|
|
|
GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
|
|
|
|
t.data + bounds.top*t.stride*2);
|
|
|
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
0, bounds.top, t.width, bounds.height(),
|
|
|
|
GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
|
|
|
|
t.data + bounds.top*t.stride*2);
|
|
|
|
} else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
|
|
|
|
t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
0, bounds.top, t.width, bounds.height(),
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE,
|
|
|
|
t.data + bounds.top*t.stride*4);
|
|
|
|
} else if (isSupportedYuvFormat(t.format)) {
|
|
|
|
// just show the Y plane of YUV buffers
|
|
|
|
glTexSubImage2D(GL_TEXTURE_2D, 0,
|
|
|
|
0, bounds.top, t.width, bounds.height(),
|
|
|
|
GL_LUMINANCE, GL_UNSIGNED_BYTE,
|
|
|
|
t.data + bounds.top*t.stride);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
}; // namespace android
|