1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_image.inl Fri Jun 15 03:10:57 2012 +0200
1.3 @@ -0,0 +1,374 @@
1.4 +/*
1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
1.6 +* All rights reserved.
1.7 +* This component and the accompanying materials are made available
1.8 +* under the terms of the License "Eclipse Public License v1.0"
1.9 +* which accompanies this distribution, and is available
1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
1.11 +*
1.12 +* Initial Contributors:
1.13 +* Nokia Corporation - initial contribution.
1.14 +*
1.15 +* Contributors:
1.16 +*
1.17 +* Description: Image implementation for the OpenGL ES API
1.18 +*
1.19 +*/
1.20 +
1.21 +
1.22 +/*!
1.23 + * \internal
1.24 + * \file
1.25 + * \brief Image implementation for the OpenGL ES API
1.26 + *
1.27 + * $Id: m3g_image.inl,v 1.11 2006/03/15 13:26:36 roimela Exp $
1.28 + */
1.29 +
1.30 +#if defined(M3G_NGL_TEXTURE_API)
1.31 +# error This file is for the OES API only
1.32 +#endif
1.33 +
1.34 +/*----------------------------------------------------------------------
1.35 + * Data types
1.36 + *--------------------------------------------------------------------*/
1.37 +
1.38 +/*!
1.39 + * \internal
1.40 + * \brief Additional data for a "large" image
1.41 + *
1.42 + * A large image is an image that is larger than the maximum texture
1.43 + * size. They basically get split into a bunch of smaller textures so
1.44 + * that we can use them for drawing backgrounds via OpenGL ES. Some
1.45 + * optimization is done to make sure we don't waste excessive amounts
1.46 + * of memory in doing so.
1.47 + */
1.48 +struct LargeImageImpl
1.49 +{
1.50 + M3Gsizei tilesX, tilesY;
1.51 + M3Gint tileWidth, tileHeight;
1.52 + M3Gbool dirty;
1.53 +
1.54 + /* The size of the tile texture name array is set dynamically upon
1.55 + * allocation, and it *must* be the last field in the
1.56 + * structure! */
1.57 + GLuint tileNames[1];
1.58 +};
1.59 +
1.60 +/*----------------------------------------------------------------------
1.61 + * Private functions
1.62 + *--------------------------------------------------------------------*/
1.63 +
1.64 +/*!
1.65 + * \internal
1.66 + * \brief Queries whether an image can be paletted internally or not
1.67 + */
1.68 +static M3Gbool m3gSupportedPaletteFormat(M3GImageFormat format)
1.69 +{
1.70 + return (format == M3G_RGB || format == M3G_RGBA);
1.71 +}
1.72 +
1.73 +/*----------------------------------------------------------------------
1.74 + * Internal functions
1.75 + *--------------------------------------------------------------------*/
1.76 +
1.77 +/*!
1.78 + * \internal
1.79 + * \brief Matches an M3G pixel format with a GL texture format
1.80 + */
1.81 +static GLenum m3gGetGLFormat(M3GPixelFormat format)
1.82 +{
1.83 + switch (format) {
1.84 + case M3G_A8:
1.85 + return GL_ALPHA;
1.86 + case M3G_L8:
1.87 + return GL_LUMINANCE;
1.88 + case M3G_LA8:
1.89 + return GL_LUMINANCE_ALPHA;
1.90 + case M3G_RGB8:
1.91 + case M3G_RGB8_32:
1.92 + case M3G_BGR8_32:
1.93 + return GL_RGB;
1.94 + case M3G_RGBA8:
1.95 + case M3G_BGRA8:
1.96 + case M3G_ARGB8:
1.97 + return GL_RGBA;
1.98 + case M3G_PALETTE8_RGB8:
1.99 + return GL_PALETTE8_RGB8_OES;
1.100 + case M3G_PALETTE8_RGBA8:
1.101 + return GL_PALETTE8_RGBA8_OES;
1.102 + default:
1.103 + return 0;
1.104 + }
1.105 +}
1.106 +
1.107 +
1.108 +/*!
1.109 + * \internal
1.110 + * \brief Destroys the additional data of a "large" image
1.111 + *
1.112 + * This can be called to save (OpenGL) memory at any time -- the data
1.113 + * will be recreated when necessary. Performance will obviously
1.114 + * suffer, though.
1.115 + */
1.116 +static void m3gDestroyLargeImage(Image *img)
1.117 +{
1.118 + LargeImage *lrg = img->large;
1.119 + M3G_VALIDATE_MEMBLOCK(lrg);
1.120 +
1.121 + m3gDeleteGLTextures(M3G_INTERFACE(img),
1.122 + lrg->tilesX * lrg->tilesY, lrg->tileNames);
1.123 + m3gFree(M3G_INTERFACE(img), img->large);
1.124 +
1.125 + img->large = NULL;
1.126 +}
1.127 +
1.128 +/*!
1.129 + * \internal
1.130 + * \brief Binds an image as an OpenGL texture object
1.131 + *
1.132 + * The image is bound to the active texture unit, which must be
1.133 + * selected outside of this function.
1.134 + */
1.135 +static void m3gBindTextureObject(Image *img, M3Gbool mipmap)
1.136 +{
1.137 + Interface *m3g;
1.138 + M3G_VALIDATE_OBJECT(img);
1.139 + m3g = M3G_INTERFACE(img);
1.140 + M3G_ASSERT(img->special == 0);
1.141 + M3G_ASSERT_NO_LOCK(m3g);
1.142 + M3G_ASSERT_GL;
1.143 +
1.144 + /* Bind the next available texture object; create a new one if it
1.145 + * doesn't exist yet. */
1.146 + {
1.147 + if (!img->texObject) {
1.148 + GLint err;
1.149 + glGenTextures(1, &img->texObject);
1.150 + err = glGetError();
1.151 + if (err == GL_OUT_OF_MEMORY) {
1.152 + m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
1.153 + return;
1.154 + }
1.155 + M3G_ASSERT(err == GL_NO_ERROR);
1.156 + M3G_LOG1(M3G_LOG_OBJECTS, "New GL texture object 0x%08X\n",
1.157 + (unsigned) img->texObject);
1.158 + img->dirty = M3G_TRUE;
1.159 + }
1.160 + glBindTexture(GL_TEXTURE_2D, img->texObject);
1.161 + }
1.162 +
1.163 + /* Upload the texture image to OpenGL if the one in the texture
1.164 + * object isn't up to date */
1.165 +
1.166 + if (img->dirty || (mipmap && img->mipmapsDirty)) {
1.167 +
1.168 + M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data));
1.169 +
1.170 + /* Reload the level 0 image if dirty. Note that paletted
1.171 + * textures are loaded as compressed, and the mipmap dirty
1.172 + * flag is only raised for non-paletted textures. */
1.173 +
1.174 + if (img->dirty) {
1.175 + M3G_ASSERT_PTR(pixels);
1.176 + if (img->paletteBytes > 0) {
1.177 + M3G_ASSERT(img->glFormat == GL_PALETTE8_RGBA8_OES
1.178 + || img->glFormat == GL_PALETTE8_RGB8_OES);
1.179 + M3G_ASSERT(mipmap == M3G_FALSE);
1.180 + glCompressedTexImage2D(GL_TEXTURE_2D,
1.181 + 0,
1.182 + img->glFormat,
1.183 + img->width, img->height,
1.184 + 0,
1.185 + img->width * img->height + img->paletteBytes,
1.186 + pixels);
1.187 + }
1.188 + else {
1.189 +# if defined(M3G_GL_ES_1_1)
1.190 + glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,
1.191 + mipmap ? GL_TRUE : GL_FALSE);
1.192 +# endif
1.193 + glTexImage2D(GL_TEXTURE_2D,
1.194 + 0,
1.195 + img->glFormat,
1.196 + img->width, img->height,
1.197 + 0,
1.198 + img->glFormat,
1.199 + GL_UNSIGNED_BYTE,
1.200 + pixels);
1.201 +# if defined(M3G_GL_ES_1_1)
1.202 + img->mipmapsDirty = M3G_FALSE;
1.203 +# else
1.204 + img->mipmapsDirty = M3G_TRUE;
1.205 +# endif
1.206 + }
1.207 + m3gUnmapObject(m3g, img->data);
1.208 + img->dirty = M3G_FALSE;
1.209 + }
1.210 +
1.211 + /* Regenerate mipmap levels if necessary; also regenerate if
1.212 + * the image will never change again, as this allows us to
1.213 + * free the user memory copy of the image and keep only the
1.214 + * mipmap pyramid in OpenGL memory, saving some in total */
1.215 +# if !defined(M3G_GL_ES_1_1)
1.216 + if (img->mipmapsDirty && (mipmap || (img->flags & M3G_DYNAMIC) == 0)) {
1.217 + int i, n;
1.218 + M3Gint w, h;
1.219 + const M3Gubyte *src;
1.220 + M3Gubyte *temp;
1.221 +
1.222 + M3G_ASSERT(!img->dirty);
1.223 +
1.224 + w = img->width;
1.225 + h = img->height;
1.226 + n = m3gGetNumMipmapLevels(w, h);
1.227 +
1.228 + temp = m3gAllocTemp(m3g,
1.229 + w * h * m3gBytesPerPixel(img->internalFormat));
1.230 + if (!temp) {
1.231 + return; /* automatic out of memory */
1.232 + }
1.233 + src = ((M3Gubyte *)m3gMapObject(m3g, img->data));
1.234 +
1.235 + for (i = 1; i < n; ++i) {
1.236 + m3gDownsample(img->internalFormat,
1.237 + src,
1.238 + &w, &h,
1.239 + temp);
1.240 + glTexImage2D(GL_TEXTURE_2D,
1.241 + i,
1.242 + img->glFormat,
1.243 + w, h,
1.244 + 0,
1.245 + img->glFormat,
1.246 + GL_UNSIGNED_BYTE,
1.247 + temp);
1.248 + src = temp;
1.249 + }
1.250 +
1.251 + m3gUnmapObject(m3g, img->data);
1.252 + m3gFreeTemp(m3g);
1.253 + img->mipmapsDirty = M3G_FALSE;
1.254 + }
1.255 +# endif /* !M3G_GL_ES_1_1 */
1.256 +
1.257 + /* Free the pixel data if we can; we've uploaded mipmap
1.258 + * levels, so OpenGL will keep them for us for the rest of the
1.259 + * lifetime of this object */
1.260 +
1.261 + if (!img->pinned && !img->mipmapsDirty) {
1.262 + m3gFreeImageData(img);
1.263 + }
1.264 +
1.265 + /* Raise out-of-memory if the OpenGL implementation ran out of
1.266 + * resources */
1.267 + {
1.268 + GLint err = glGetError();
1.269 +
1.270 + if (err == GL_OUT_OF_MEMORY) {
1.271 + m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
1.272 + }
1.273 + else if (err != GL_NO_ERROR) {
1.274 + M3G_ASSERT(M3G_FALSE);
1.275 + }
1.276 + }
1.277 + }
1.278 +}
1.279 +
1.280 +/*!
1.281 + * \internal
1.282 + * \brief Releases one of the texture objects bound for this image
1.283 + *
1.284 + * This assumes that the texture unit the image was bound to is
1.285 + * current.
1.286 + */
1.287 +static void m3gReleaseTextureImage(Image *img)
1.288 +{
1.289 + M3G_VALIDATE_OBJECT(img);
1.290 + M3G_UNREF(img);
1.291 +
1.292 + glBindTexture(GL_TEXTURE_2D, 0);
1.293 +
1.294 + M3G_ASSERT_GL;
1.295 +}
1.296 +
1.297 +/*!
1.298 + * \internal
1.299 + * \brief Copies an image from the bottom left corner of the frame
1.300 + * buffer
1.301 + *
1.302 + */
1.303 +static void m3gCopyFrameBufferImage(Image *img)
1.304 +{
1.305 + Interface *m3g;
1.306 + M3Gubyte *pixels;
1.307 + M3G_VALIDATE_OBJECT(img);
1.308 + M3G_ASSERT_GL;
1.309 +
1.310 + m3g = M3G_INTERFACE(img);
1.311 +
1.312 + {
1.313 + int row;
1.314 + M3Gsizei stride = img->width * m3gBytesPerPixel(img->internalFormat);
1.315 +
1.316 + /* An RGBA image we can copy straight into the user memory buffer */
1.317 +
1.318 + if (img->internalFormat == M3G_RGBA8) {
1.319 + pixels = m3gMapObject(m3g, img->data);
1.320 + for (row = 0; row < img->height; ++row) {
1.321 + glReadPixels(0, img->height - row - 1,
1.322 + img->width, 1,
1.323 + GL_RGBA, GL_UNSIGNED_BYTE,
1.324 + pixels + row * stride);
1.325 + }
1.326 + m3gUnmapObject(m3g, img->data);
1.327 + }
1.328 + else {
1.329 +
1.330 + /* For non-RGBA images, we must do a format conversion from
1.331 + * the RGBA returned by ReadPixels to the destination
1.332 + * format. We do this one scanline at a time to spare memory.
1.333 + */
1.334 +
1.335 + M3Gubyte *temp = m3gAllocTemp(m3g, img->width * 4);
1.336 + if (!temp) {
1.337 + return; /* out of memory */
1.338 + }
1.339 + pixels = m3gMapObject(m3g, img->data);
1.340 +
1.341 + for (row = 0; row < img->height; ++row) {
1.342 + glReadPixels(0, img->height - row - 1,
1.343 + img->width, 1,
1.344 + GL_RGBA, GL_UNSIGNED_BYTE,
1.345 + temp);
1.346 + m3gConvertPixels(M3G_RGBA8, temp,
1.347 + img->internalFormat, pixels + row * stride,
1.348 + img->width);
1.349 + }
1.350 + m3gUnmapObject(m3g, img->data);
1.351 + m3gFreeTemp(m3g);
1.352 + }
1.353 + }
1.354 + M3G_ASSERT_GL;
1.355 +
1.356 + m3gInvalidateImage(img);
1.357 +}
1.358 +
1.359 +/*!
1.360 + * \internal
1.361 + * \brief Draws any RGB or RGBA image into the bottom left corner of
1.362 + * the frame buffer
1.363 + */
1.364 +static void m3gDrawFrameBufferImage(RenderContext *ctx, const Image *img)
1.365 +{
1.366 + M3G_VALIDATE_OBJECT(img);
1.367 + {
1.368 + const M3Gubyte *pixels = m3gMapObject(M3G_INTERFACE(img), img->data);
1.369 + m3gBlitFrameBufferPixels(ctx,
1.370 + 0, 0,
1.371 + img->width, img->height,
1.372 + img->internalFormat,
1.373 + m3gGetImageStride(img),
1.374 + pixels);
1.375 + m3gUnmapObject(M3G_INTERFACE(img), img->data);
1.376 + }
1.377 +}