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