os/graphics/m3g/m3gcore11/src/m3g_image.inl
author sl
Tue, 10 Jun 2014 14:32:02 +0200
changeset 1 260cb5ec6c19
permissions -rw-r--r--
Update contrib.
     1 /*
     2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     3 * All rights reserved.
     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".
     8 *
     9 * Initial Contributors:
    10 * Nokia Corporation - initial contribution.
    11 *
    12 * Contributors:
    13 *
    14 * Description: Image implementation for the OpenGL ES API
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Image implementation for the OpenGL ES API
    23  *
    24  * $Id: m3g_image.inl,v 1.11 2006/03/15 13:26:36 roimela Exp $
    25  */
    26 
    27 #if defined(M3G_NGL_TEXTURE_API)
    28 #   error This file is for the OES API only
    29 #endif
    30 
    31 /*----------------------------------------------------------------------
    32  * Data types
    33  *--------------------------------------------------------------------*/
    34 
    35 /*!
    36  * \internal
    37  * \brief Additional data for a "large" image
    38  *
    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.
    44  */
    45 struct LargeImageImpl
    46 {
    47     M3Gsizei tilesX, tilesY;
    48     M3Gint tileWidth, tileHeight;
    49     M3Gbool dirty;
    50 
    51     /* The size of the tile texture name array is set dynamically upon
    52      * allocation, and it *must* be the last field in the
    53      * structure! */
    54     GLuint tileNames[1];
    55 };
    56 
    57 /*----------------------------------------------------------------------
    58  * Private functions
    59  *--------------------------------------------------------------------*/
    60 
    61 /*!
    62  * \internal
    63  * \brief Queries whether an image can be paletted internally or not
    64  */
    65 static M3Gbool m3gSupportedPaletteFormat(M3GImageFormat format)
    66 {
    67     return (format == M3G_RGB || format == M3G_RGBA);
    68 }
    69 
    70 /*----------------------------------------------------------------------
    71  * Internal functions
    72  *--------------------------------------------------------------------*/
    73 
    74 /*!
    75  * \internal
    76  * \brief Matches an M3G pixel format with a GL texture format
    77  */
    78 static GLenum m3gGetGLFormat(M3GPixelFormat format)
    79 {
    80     switch (format) {
    81     case M3G_A8:
    82         return GL_ALPHA;
    83     case M3G_L8:
    84         return GL_LUMINANCE;
    85     case M3G_LA8:
    86         return GL_LUMINANCE_ALPHA;
    87     case M3G_RGB8:
    88     case M3G_RGB8_32:
    89     case M3G_BGR8_32:
    90         return GL_RGB;
    91     case M3G_RGBA8:
    92     case M3G_BGRA8:
    93     case M3G_ARGB8:
    94         return GL_RGBA;
    95     case M3G_PALETTE8_RGB8:
    96         return GL_PALETTE8_RGB8_OES;
    97     case M3G_PALETTE8_RGBA8:
    98         return GL_PALETTE8_RGBA8_OES;
    99     default:
   100         return 0;
   101     }
   102 }
   103 
   104 
   105 /*!
   106  * \internal
   107  * \brief Destroys the additional data of a "large" image
   108  *
   109  * This can be called to save (OpenGL) memory at any time -- the data
   110  * will be recreated when necessary.  Performance will obviously
   111  * suffer, though.
   112  */
   113 static void m3gDestroyLargeImage(Image *img)
   114 {
   115     LargeImage *lrg = img->large;
   116     M3G_VALIDATE_MEMBLOCK(lrg);
   117 
   118     m3gDeleteGLTextures(M3G_INTERFACE(img),
   119                         lrg->tilesX * lrg->tilesY, lrg->tileNames);
   120     m3gFree(M3G_INTERFACE(img), img->large);
   121     
   122     img->large = NULL;
   123 }
   124 
   125 /*!
   126  * \internal
   127  * \brief Binds an image as an OpenGL texture object
   128  *
   129  * The image is bound to the active texture unit, which must be
   130  * selected outside of this function.
   131  */
   132 static void m3gBindTextureObject(Image *img, M3Gbool mipmap)
   133 {
   134     Interface *m3g;
   135     M3G_VALIDATE_OBJECT(img);
   136     m3g = M3G_INTERFACE(img);
   137     M3G_ASSERT(img->special == 0);
   138     M3G_ASSERT_NO_LOCK(m3g);
   139     M3G_ASSERT_GL;
   140 
   141     /* Bind the next available texture object; create a new one if it
   142      * doesn't exist yet. */
   143     {
   144         if (!img->texObject) {
   145             GLint err;
   146             glGenTextures(1, &img->texObject);
   147             err = glGetError();
   148             if (err == GL_OUT_OF_MEMORY) {
   149                 m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
   150                 return;
   151             }
   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;
   156         }
   157         glBindTexture(GL_TEXTURE_2D, img->texObject);
   158     }
   159 
   160     /* Upload the texture image to OpenGL if the one in the texture
   161      * object isn't up to date */
   162 
   163     if (img->dirty || (mipmap && img->mipmapsDirty)) {
   164 
   165         M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data));
   166 
   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. */
   170 
   171         if (img->dirty) {
   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,
   178                                        0,
   179                                        img->glFormat,
   180                                        img->width, img->height,
   181                                        0,
   182                                        img->width * img->height + img->paletteBytes,
   183                                        pixels);
   184             }
   185             else {
   186 #               if defined(M3G_GL_ES_1_1)
   187                 glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,
   188                                 mipmap ? GL_TRUE : GL_FALSE);
   189 #               endif
   190                 glTexImage2D(GL_TEXTURE_2D,
   191                              0,
   192                              img->glFormat,
   193                              img->width, img->height,
   194                              0,
   195                              img->glFormat,
   196                              GL_UNSIGNED_BYTE,
   197                              pixels);
   198 #               if defined(M3G_GL_ES_1_1)
   199                 img->mipmapsDirty = M3G_FALSE;
   200 #               else
   201                 img->mipmapsDirty = M3G_TRUE;
   202 #               endif
   203             }
   204             m3gUnmapObject(m3g, img->data);
   205             img->dirty = M3G_FALSE;
   206         }
   207 
   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)) {
   214             int i, n;
   215             M3Gint w, h;
   216             const M3Gubyte *src;
   217             M3Gubyte *temp;
   218 
   219             M3G_ASSERT(!img->dirty);
   220 
   221             w = img->width;
   222             h = img->height;
   223             n = m3gGetNumMipmapLevels(w, h);
   224 
   225             temp = m3gAllocTemp(m3g,
   226                                 w * h * m3gBytesPerPixel(img->internalFormat));
   227             if (!temp) {
   228                 return; /* automatic out of memory */
   229             }
   230             src = ((M3Gubyte *)m3gMapObject(m3g, img->data));
   231 
   232             for (i = 1; i < n; ++i) {
   233                 m3gDownsample(img->internalFormat,
   234                               src,
   235                               &w, &h,
   236                               temp);
   237                 glTexImage2D(GL_TEXTURE_2D,
   238                              i,
   239                              img->glFormat,
   240                              w, h,
   241                              0,
   242                              img->glFormat,
   243                              GL_UNSIGNED_BYTE,
   244                              temp);
   245                 src = temp;
   246             }
   247 
   248             m3gUnmapObject(m3g, img->data);
   249             m3gFreeTemp(m3g);
   250             img->mipmapsDirty = M3G_FALSE;
   251         }
   252 #       endif /* !M3G_GL_ES_1_1 */
   253 
   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 */
   257 
   258         if (!img->pinned && !img->mipmapsDirty) {
   259             m3gFreeImageData(img);
   260         }
   261 
   262         /* Raise out-of-memory if the OpenGL implementation ran out of
   263          * resources */
   264         {
   265             GLint err = glGetError();
   266             
   267             if (err == GL_OUT_OF_MEMORY) {
   268                 m3gRaiseError(M3G_INTERFACE(img), M3G_OUT_OF_MEMORY);
   269             }
   270             else if (err != GL_NO_ERROR) {
   271                 M3G_ASSERT(M3G_FALSE);
   272             }
   273         }
   274     }
   275 }
   276 
   277 /*!
   278  * \internal
   279  * \brief Releases one of the texture objects bound for this image
   280  *
   281  * This assumes that the texture unit the image was bound to is
   282  * current.
   283  */
   284 static void m3gReleaseTextureImage(Image *img)
   285 {
   286     M3G_VALIDATE_OBJECT(img);
   287     M3G_UNREF(img);
   288     
   289     glBindTexture(GL_TEXTURE_2D, 0);
   290     
   291     M3G_ASSERT_GL;
   292 }
   293 
   294 /*!
   295  * \internal
   296  * \brief Copies an image from the bottom left corner of the frame
   297  * buffer
   298  *
   299  */
   300 static void m3gCopyFrameBufferImage(Image *img)
   301 {
   302     Interface *m3g;
   303     M3Gubyte *pixels;
   304     M3G_VALIDATE_OBJECT(img);
   305     M3G_ASSERT_GL;
   306 
   307     m3g = M3G_INTERFACE(img);
   308     
   309     {
   310         int row;
   311         M3Gsizei stride = img->width * m3gBytesPerPixel(img->internalFormat);
   312         
   313         /* An RGBA image we can copy straight into the user memory buffer */
   314 
   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,
   319                              img->width, 1,
   320                              GL_RGBA, GL_UNSIGNED_BYTE,
   321                              pixels + row * stride);
   322             }
   323             m3gUnmapObject(m3g, img->data);
   324         }
   325         else {
   326             
   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.
   330              */
   331             
   332             M3Gubyte *temp = m3gAllocTemp(m3g, img->width * 4);
   333             if (!temp) {
   334                 return; /* out of memory */
   335             }
   336             pixels = m3gMapObject(m3g, img->data);
   337             
   338             for (row = 0; row < img->height; ++row) {
   339                 glReadPixels(0, img->height - row - 1,
   340                              img->width, 1,
   341                              GL_RGBA, GL_UNSIGNED_BYTE,
   342                              temp);
   343                 m3gConvertPixels(M3G_RGBA8, temp,
   344                                  img->internalFormat, pixels + row * stride,
   345                                  img->width);
   346             }
   347             m3gUnmapObject(m3g, img->data);
   348             m3gFreeTemp(m3g);
   349         }
   350     }
   351     M3G_ASSERT_GL;
   352     
   353     m3gInvalidateImage(img);
   354 }
   355 
   356 /*!
   357  * \internal
   358  * \brief Draws any RGB or RGBA image into the bottom left corner of
   359  * the frame buffer
   360  */
   361 static void m3gDrawFrameBufferImage(RenderContext *ctx, const Image *img)
   362 {
   363     M3G_VALIDATE_OBJECT(img);
   364     {
   365         const M3Gubyte *pixels = m3gMapObject(M3G_INTERFACE(img), img->data);
   366         m3gBlitFrameBufferPixels(ctx,
   367                                  0, 0,
   368                                  img->width, img->height,
   369                                  img->internalFormat,
   370                                  m3gGetImageStride(img),
   371                                  pixels);
   372         m3gUnmapObject(M3G_INTERFACE(img), img->data);
   373     }
   374 }