Update contrib.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
4 * This component and the accompanying materials are made available
5 * under the terms of the License "Eclipse Public License v1.0"
6 * which accompanies this distribution, and is available
7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Image implementation for the OpenGL ES API
22 * \brief Image implementation for the OpenGL ES API
24 * $Id: m3g_image.inl,v 1.11 2006/03/15 13:26:36 roimela Exp $
27 #if defined(M3G_NGL_TEXTURE_API)
28 # error This file is for the OES API only
31 /*----------------------------------------------------------------------
33 *--------------------------------------------------------------------*/
37 * \brief Additional data for a "large" image
39 * A large image is an image that is larger than the maximum texture
40 * size. They basically get split into a bunch of smaller textures so
41 * that we can use them for drawing backgrounds via OpenGL ES. Some
42 * optimization is done to make sure we don't waste excessive amounts
43 * of memory in doing so.
47 M3Gsizei tilesX, tilesY;
48 M3Gint tileWidth, tileHeight;
51 /* The size of the tile texture name array is set dynamically upon
52 * allocation, and it *must* be the last field in the
57 /*----------------------------------------------------------------------
59 *--------------------------------------------------------------------*/
63 * \brief Queries whether an image can be paletted internally or not
65 static M3Gbool m3gSupportedPaletteFormat(M3GImageFormat format)
67 return (format == M3G_RGB || format == M3G_RGBA);
70 /*----------------------------------------------------------------------
72 *--------------------------------------------------------------------*/
76 * \brief Matches an M3G pixel format with a GL texture format
78 static GLenum m3gGetGLFormat(M3GPixelFormat format)
86 return GL_LUMINANCE_ALPHA;
95 case M3G_PALETTE8_RGB8:
96 return GL_PALETTE8_RGB8_OES;
97 case M3G_PALETTE8_RGBA8:
98 return GL_PALETTE8_RGBA8_OES;
107 * \brief Destroys the additional data of a "large" image
109 * This can be called to save (OpenGL) memory at any time -- the data
110 * will be recreated when necessary. Performance will obviously
113 static void m3gDestroyLargeImage(Image *img)
115 LargeImage *lrg = img->large;
116 M3G_VALIDATE_MEMBLOCK(lrg);
118 m3gDeleteGLTextures(M3G_INTERFACE(img),
119 lrg->tilesX * lrg->tilesY, lrg->tileNames);
120 m3gFree(M3G_INTERFACE(img), img->large);
127 * \brief Binds an image as an OpenGL texture object
129 * The image is bound to the active texture unit, which must be
130 * selected outside of this function.
132 static void m3gBindTextureObject(Image *img, M3Gbool mipmap)
135 M3G_VALIDATE_OBJECT(img);
136 m3g = M3G_INTERFACE(img);
137 M3G_ASSERT(img->special == 0);
138 M3G_ASSERT_NO_LOCK(m3g);
141 /* Bind the next available texture object; create a new one if it
142 * doesn't exist yet. */
144 if (!img->texObject) {
146 glGenTextures(1, &img->texObject);
148 if (err == GL_OUT_OF_MEMORY) {
149 m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
152 M3G_ASSERT(err == GL_NO_ERROR);
153 M3G_LOG1(M3G_LOG_OBJECTS, "New GL texture object 0x%08X\n",
154 (unsigned) img->texObject);
155 img->dirty = M3G_TRUE;
157 glBindTexture(GL_TEXTURE_2D, img->texObject);
160 /* Upload the texture image to OpenGL if the one in the texture
161 * object isn't up to date */
163 if (img->dirty || (mipmap && img->mipmapsDirty)) {
165 M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data));
167 /* Reload the level 0 image if dirty. Note that paletted
168 * textures are loaded as compressed, and the mipmap dirty
169 * flag is only raised for non-paletted textures. */
172 M3G_ASSERT_PTR(pixels);
173 if (img->paletteBytes > 0) {
174 M3G_ASSERT(img->glFormat == GL_PALETTE8_RGBA8_OES
175 || img->glFormat == GL_PALETTE8_RGB8_OES);
176 M3G_ASSERT(mipmap == M3G_FALSE);
177 glCompressedTexImage2D(GL_TEXTURE_2D,
180 img->width, img->height,
182 img->width * img->height + img->paletteBytes,
186 # if defined(M3G_GL_ES_1_1)
187 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,
188 mipmap ? GL_TRUE : GL_FALSE);
190 glTexImage2D(GL_TEXTURE_2D,
193 img->width, img->height,
198 # if defined(M3G_GL_ES_1_1)
199 img->mipmapsDirty = M3G_FALSE;
201 img->mipmapsDirty = M3G_TRUE;
204 m3gUnmapObject(m3g, img->data);
205 img->dirty = M3G_FALSE;
208 /* Regenerate mipmap levels if necessary; also regenerate if
209 * the image will never change again, as this allows us to
210 * free the user memory copy of the image and keep only the
211 * mipmap pyramid in OpenGL memory, saving some in total */
212 # if !defined(M3G_GL_ES_1_1)
213 if (img->mipmapsDirty && (mipmap || (img->flags & M3G_DYNAMIC) == 0)) {
219 M3G_ASSERT(!img->dirty);
223 n = m3gGetNumMipmapLevels(w, h);
225 temp = m3gAllocTemp(m3g,
226 w * h * m3gBytesPerPixel(img->internalFormat));
228 return; /* automatic out of memory */
230 src = ((M3Gubyte *)m3gMapObject(m3g, img->data));
232 for (i = 1; i < n; ++i) {
233 m3gDownsample(img->internalFormat,
237 glTexImage2D(GL_TEXTURE_2D,
248 m3gUnmapObject(m3g, img->data);
250 img->mipmapsDirty = M3G_FALSE;
252 # endif /* !M3G_GL_ES_1_1 */
254 /* Free the pixel data if we can; we've uploaded mipmap
255 * levels, so OpenGL will keep them for us for the rest of the
256 * lifetime of this object */
258 if (!img->pinned && !img->mipmapsDirty) {
259 m3gFreeImageData(img);
262 /* Raise out-of-memory if the OpenGL implementation ran out of
265 GLint err = glGetError();
267 if (err == GL_OUT_OF_MEMORY) {
268 m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
270 else if (err != GL_NO_ERROR) {
271 M3G_ASSERT(M3G_FALSE);
279 * \brief Releases one of the texture objects bound for this image
281 * This assumes that the texture unit the image was bound to is
284 static void m3gReleaseTextureImage(Image *img)
286 M3G_VALIDATE_OBJECT(img);
289 glBindTexture(GL_TEXTURE_2D, 0);
296 * \brief Copies an image from the bottom left corner of the frame
300 static void m3gCopyFrameBufferImage(Image *img)
304 M3G_VALIDATE_OBJECT(img);
307 m3g = M3G_INTERFACE(img);
311 M3Gsizei stride = img->width * m3gBytesPerPixel(img->internalFormat);
313 /* An RGBA image we can copy straight into the user memory buffer */
315 if (img->internalFormat == M3G_RGBA8) {
316 pixels = m3gMapObject(m3g, img->data);
317 for (row = 0; row < img->height; ++row) {
318 glReadPixels(0, img->height - row - 1,
320 GL_RGBA, GL_UNSIGNED_BYTE,
321 pixels + row * stride);
323 m3gUnmapObject(m3g, img->data);
327 /* For non-RGBA images, we must do a format conversion from
328 * the RGBA returned by ReadPixels to the destination
329 * format. We do this one scanline at a time to spare memory.
332 M3Gubyte *temp = m3gAllocTemp(m3g, img->width * 4);
334 return; /* out of memory */
336 pixels = m3gMapObject(m3g, img->data);
338 for (row = 0; row < img->height; ++row) {
339 glReadPixels(0, img->height - row - 1,
341 GL_RGBA, GL_UNSIGNED_BYTE,
343 m3gConvertPixels(M3G_RGBA8, temp,
344 img->internalFormat, pixels + row * stride,
347 m3gUnmapObject(m3g, img->data);
353 m3gInvalidateImage(img);
358 * \brief Draws any RGB or RGBA image into the bottom left corner of
361 static void m3gDrawFrameBufferImage(RenderContext *ctx, const Image *img)
363 M3G_VALIDATE_OBJECT(img);
365 const M3Gubyte *pixels = m3gMapObject(M3G_INTERFACE(img), img->data);
366 m3gBlitFrameBufferPixels(ctx,
368 img->width, img->height,
370 m3gGetImageStride(img),
372 m3gUnmapObject(M3G_INTERFACE(img), img->data);