--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/m3g/m3gcore11/src/m3g_image.inl Tue Feb 02 01:47:50 2010 +0200
@@ -0,0 +1,373 @@
+/*
+* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of the License "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description: Image implementation for the OpenGL ES API
+*
+*/
+
+
+/*!
+ * \internal
+ * \file
+ * \brief Image implementation for the OpenGL ES API
+ *
+ * $Id: m3g_image.inl,v 1.11 2006/03/15 13:26:36 roimela Exp $
+ */
+
+#if defined(M3G_NGL_TEXTURE_API)
+# error This file is for the OES API only
+#endif
+
+/*----------------------------------------------------------------------
+ * Data types
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Additional data for a "large" image
+ *
+ * A large image is an image that is larger than the maximum texture
+ * size. They basically get split into a bunch of smaller textures so
+ * that we can use them for drawing backgrounds via OpenGL ES. Some
+ * optimization is done to make sure we don't waste excessive amounts
+ * of memory in doing so.
+ */
+struct LargeImageImpl
+{
+ M3Gsizei tilesX, tilesY;
+ M3Gint tileWidth, tileHeight;
+ M3Gbool dirty;
+
+ /* The size of the tile texture name array is set dynamically upon
+ * allocation, and it *must* be the last field in the
+ * structure! */
+ GLuint tileNames[1];
+};
+
+/*----------------------------------------------------------------------
+ * Private functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Queries whether an image can be paletted internally or not
+ */
+static M3Gbool m3gSupportedPaletteFormat(M3GImageFormat format)
+{
+ return (format == M3G_RGB || format == M3G_RGBA);
+}
+
+/*----------------------------------------------------------------------
+ * Internal functions
+ *--------------------------------------------------------------------*/
+
+/*!
+ * \internal
+ * \brief Matches an M3G pixel format with a GL texture format
+ */
+static GLenum m3gGetGLFormat(M3GPixelFormat format)
+{
+ switch (format) {
+ case M3G_A8:
+ return GL_ALPHA;
+ case M3G_L8:
+ return GL_LUMINANCE;
+ case M3G_LA8:
+ return GL_LUMINANCE_ALPHA;
+ case M3G_RGB8:
+ case M3G_RGB8_32:
+ case M3G_BGR8_32:
+ return GL_RGB;
+ case M3G_RGBA8:
+ case M3G_BGRA8:
+ return GL_RGBA;
+ case M3G_PALETTE8_RGB8:
+ return GL_PALETTE8_RGB8_OES;
+ case M3G_PALETTE8_RGBA8:
+ return GL_PALETTE8_RGBA8_OES;
+ default:
+ return 0;
+ }
+}
+
+
+/*!
+ * \internal
+ * \brief Destroys the additional data of a "large" image
+ *
+ * This can be called to save (OpenGL) memory at any time -- the data
+ * will be recreated when necessary. Performance will obviously
+ * suffer, though.
+ */
+static void m3gDestroyLargeImage(Image *img)
+{
+ LargeImage *lrg = img->large;
+ M3G_VALIDATE_MEMBLOCK(lrg);
+
+ m3gDeleteGLTextures(M3G_INTERFACE(img),
+ lrg->tilesX * lrg->tilesY, lrg->tileNames);
+ m3gFree(M3G_INTERFACE(img), img->large);
+
+ img->large = NULL;
+}
+
+/*!
+ * \internal
+ * \brief Binds an image as an OpenGL texture object
+ *
+ * The image is bound to the active texture unit, which must be
+ * selected outside of this function.
+ */
+static void m3gBindTextureObject(Image *img, M3Gbool mipmap)
+{
+ Interface *m3g;
+ M3G_VALIDATE_OBJECT(img);
+ m3g = M3G_INTERFACE(img);
+ M3G_ASSERT(img->special == 0);
+ M3G_ASSERT_NO_LOCK(m3g);
+ M3G_ASSERT_GL;
+
+ /* Bind the next available texture object; create a new one if it
+ * doesn't exist yet. */
+ {
+ if (!img->texObject) {
+ GLint err;
+ glGenTextures(1, &img->texObject);
+ err = glGetError();
+ if (err == GL_OUT_OF_MEMORY) {
+ m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
+ return;
+ }
+ M3G_ASSERT(err == GL_NO_ERROR);
+ M3G_LOG1(M3G_LOG_OBJECTS, "New GL texture object 0x%08X\n",
+ (unsigned) img->texObject);
+ img->dirty = M3G_TRUE;
+ }
+ glBindTexture(GL_TEXTURE_2D, img->texObject);
+ }
+
+ /* Upload the texture image to OpenGL if the one in the texture
+ * object isn't up to date */
+
+ if (img->dirty || (mipmap && img->mipmapsDirty)) {
+
+ M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data));
+
+ /* Reload the level 0 image if dirty. Note that paletted
+ * textures are loaded as compressed, and the mipmap dirty
+ * flag is only raised for non-paletted textures. */
+
+ if (img->dirty) {
+ M3G_ASSERT_PTR(pixels);
+ if (img->paletteBytes > 0) {
+ M3G_ASSERT(img->glFormat == GL_PALETTE8_RGBA8_OES
+ || img->glFormat == GL_PALETTE8_RGB8_OES);
+ M3G_ASSERT(mipmap == M3G_FALSE);
+ glCompressedTexImage2D(GL_TEXTURE_2D,
+ 0,
+ img->glFormat,
+ img->width, img->height,
+ 0,
+ img->width * img->height + img->paletteBytes,
+ pixels);
+ }
+ else {
+# if defined(M3G_GL_ES_1_1)
+ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,
+ mipmap ? GL_TRUE : GL_FALSE);
+# endif
+ glTexImage2D(GL_TEXTURE_2D,
+ 0,
+ img->glFormat,
+ img->width, img->height,
+ 0,
+ img->glFormat,
+ GL_UNSIGNED_BYTE,
+ pixels);
+# if defined(M3G_GL_ES_1_1)
+ img->mipmapsDirty = M3G_FALSE;
+# else
+ img->mipmapsDirty = M3G_TRUE;
+# endif
+ }
+ m3gUnmapObject(m3g, img->data);
+ img->dirty = M3G_FALSE;
+ }
+
+ /* Regenerate mipmap levels if necessary; also regenerate if
+ * the image will never change again, as this allows us to
+ * free the user memory copy of the image and keep only the
+ * mipmap pyramid in OpenGL memory, saving some in total */
+# if !defined(M3G_GL_ES_1_1)
+ if (img->mipmapsDirty && (mipmap || (img->flags & M3G_DYNAMIC) == 0)) {
+ int i, n;
+ M3Gint w, h;
+ const M3Gubyte *src;
+ M3Gubyte *temp;
+
+ M3G_ASSERT(!img->dirty);
+
+ w = img->width;
+ h = img->height;
+ n = m3gGetNumMipmapLevels(w, h);
+
+ temp = m3gAllocTemp(m3g,
+ w * h * m3gBytesPerPixel(img->internalFormat));
+ if (!temp) {
+ return; /* automatic out of memory */
+ }
+ src = ((M3Gubyte *)m3gMapObject(m3g, img->data));
+
+ for (i = 1; i < n; ++i) {
+ m3gDownsample(img->internalFormat,
+ src,
+ &w, &h,
+ temp);
+ glTexImage2D(GL_TEXTURE_2D,
+ i,
+ img->glFormat,
+ w, h,
+ 0,
+ img->glFormat,
+ GL_UNSIGNED_BYTE,
+ temp);
+ src = temp;
+ }
+
+ m3gUnmapObject(m3g, img->data);
+ m3gFreeTemp(m3g);
+ img->mipmapsDirty = M3G_FALSE;
+ }
+# endif /* !M3G_GL_ES_1_1 */
+
+ /* Free the pixel data if we can; we've uploaded mipmap
+ * levels, so OpenGL will keep them for us for the rest of the
+ * lifetime of this object */
+
+ if (!img->pinned && !img->mipmapsDirty) {
+ m3gFreeImageData(img);
+ }
+
+ /* Raise out-of-memory if the OpenGL implementation ran out of
+ * resources */
+ {
+ GLint err = glGetError();
+
+ if (err == GL_OUT_OF_MEMORY) {
+ m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
+ }
+ else if (err != GL_NO_ERROR) {
+ M3G_ASSERT(M3G_FALSE);
+ }
+ }
+ }
+}
+
+/*!
+ * \internal
+ * \brief Releases one of the texture objects bound for this image
+ *
+ * This assumes that the texture unit the image was bound to is
+ * current.
+ */
+static void m3gReleaseTextureImage(Image *img)
+{
+ M3G_VALIDATE_OBJECT(img);
+ M3G_UNREF(img);
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+
+ M3G_ASSERT_GL;
+}
+
+/*!
+ * \internal
+ * \brief Copies an image from the bottom left corner of the frame
+ * buffer
+ *
+ */
+static void m3gCopyFrameBufferImage(Image *img)
+{
+ Interface *m3g;
+ M3Gubyte *pixels;
+ M3G_VALIDATE_OBJECT(img);
+ M3G_ASSERT_GL;
+
+ m3g = M3G_INTERFACE(img);
+
+ {
+ int row;
+ M3Gsizei stride = img->width * m3gBytesPerPixel(img->internalFormat);
+
+ /* An RGBA image we can copy straight into the user memory buffer */
+
+ if (img->internalFormat == M3G_RGBA8) {
+ pixels = m3gMapObject(m3g, img->data);
+ for (row = 0; row < img->height; ++row) {
+ glReadPixels(0, img->height - row - 1,
+ img->width, 1,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ pixels + row * stride);
+ }
+ m3gUnmapObject(m3g, img->data);
+ }
+ else {
+
+ /* For non-RGBA images, we must do a format conversion from
+ * the RGBA returned by ReadPixels to the destination
+ * format. We do this one scanline at a time to spare memory.
+ */
+
+ M3Gubyte *temp = m3gAllocTemp(m3g, img->width * 4);
+ if (!temp) {
+ return; /* out of memory */
+ }
+ pixels = m3gMapObject(m3g, img->data);
+
+ for (row = 0; row < img->height; ++row) {
+ glReadPixels(0, img->height - row - 1,
+ img->width, 1,
+ GL_RGBA, GL_UNSIGNED_BYTE,
+ temp);
+ m3gConvertPixels(M3G_RGBA8, temp,
+ img->internalFormat, pixels + row * stride,
+ img->width);
+ }
+ m3gUnmapObject(m3g, img->data);
+ m3gFreeTemp(m3g);
+ }
+ }
+ M3G_ASSERT_GL;
+
+ m3gInvalidateImage(img);
+}
+
+/*!
+ * \internal
+ * \brief Draws any RGB or RGBA image into the bottom left corner of
+ * the frame buffer
+ */
+static void m3gDrawFrameBufferImage(RenderContext *ctx, const Image *img)
+{
+ M3G_VALIDATE_OBJECT(img);
+ {
+ const M3Gubyte *pixels = m3gMapObject(M3G_INTERFACE(img), img->data);
+ m3gBlitFrameBufferPixels(ctx,
+ 0, 0,
+ img->width, img->height,
+ img->internalFormat,
+ m3gGetImageStride(img),
+ pixels);
+ m3gUnmapObject(M3G_INTERFACE(img), img->data);
+ }
+}