os/graphics/m3g/m3gcore11/src/m3g_image.c
author sl@SLION-WIN7.fritz.box
Fri, 15 Jun 2012 03:10:57 +0200
changeset 0 bde4ae8d615e
permissions -rw-r--r--
First public contribution.
     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
    15 *
    16 */
    17 
    18 
    19 /*!
    20  * \internal
    21  * \file
    22  * \brief Image implementation
    23  *
    24  */
    25 
    26 #ifndef M3G_CORE_INCLUDE
    27 #   error included by m3g_core.c; do not compile separately.
    28 #endif
    29 
    30 #include "m3g_image.h"
    31 #include "m3g_texture.h"
    32 
    33 /* Declare prototypes for some of the helper functions called from the
    34  * platform-dependent code included below */
    35 
    36 static M3Gint  m3gBytesPerPixel(M3GPixelFormat format);
    37 static M3Gint  m3gGetNumMipmapLevels(M3Gint w, M3Gint h);
    38 static void m3gDownsample(M3GPixelFormat format, const M3Gubyte *srcPixels, M3Gint *pw, M3Gint *ph, M3Gubyte *dstPixels);
    39 static void m3gFreeImageData(Image *img);
    40 
    41 /* Include platform-dependent functionality */
    42 #include "m3g_image.inl"
    43 
    44 /* Size of the buffer used in pixel format conversions; this should be
    45  * an even number */
    46 #define SPAN_BUFFER_SIZE 32
    47 
    48 M3G_CT_ASSERT((SPAN_BUFFER_SIZE & 1) == 0);
    49 
    50 /*----------------------------------------------------------------------
    51  * Private functions
    52  *--------------------------------------------------------------------*/
    53 
    54 /*!
    55  * \internal
    56  * \brief Destroys this Image object.
    57  *
    58  * \param obj Image object
    59  */
    60 static void m3gDestroyImage(Object *obj)
    61 {
    62     Image *image = (Image*)obj;
    63     Interface *m3g = M3G_INTERFACE(image);
    64     M3G_VALIDATE_OBJECT(image);
    65 
    66     if (!image->copyOf) {
    67         m3gFreeObject(m3g, image->data);
    68         m3gFreeObject(m3g, image->mipData);
    69     }
    70     M3G_ASSIGN_REF(image->copyOf, NULL);
    71     
    72     if (image->powerOfTwo != image) {
    73         M3G_ASSIGN_REF(image->powerOfTwo, NULL);
    74     }
    75 
    76 #   if !defined(M3G_NGL_TEXTURE_API)
    77     if (image->texObject) {
    78         m3gDeleteGLTextures(m3g, 1, &image->texObject);
    79     }
    80     if (image->large != NULL) {
    81         m3gDestroyLargeImage(image);
    82     }
    83 #   endif
    84 
    85     m3gDestroyObject(obj);
    86 }
    87 
    88 /*--------------------------------------------------------------------*/
    89 
    90 #define RED(argb)   (((argb) >> 16) & 0xFFu)
    91 #define GREEN(argb) (((argb) >>  8) & 0xFFu)
    92 #define BLUE(argb)  ( (argb)        & 0xFFu)
    93 #define ALPHA(argb) (((argb) >> 24) & 0xFFu)
    94 
    95 #define RGBSUM(argb) (0x4CB2u * RED(argb) +     \
    96                       0x9691u * GREEN(argb) +   \
    97                       0x1D3Eu * BLUE(argb))
    98 
    99 /*!
   100  * \internal \brief ARGB -> A
   101  */
   102 static void convertARGBToA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   103 {
   104     while (count--) {
   105         *dst++ = (M3Gubyte) ALPHA(*src++);
   106     }
   107 }
   108 
   109 /*!
   110  * \internal \brief ARGB -> L
   111  */
   112 static void convertARGBToL8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   113 {
   114     while (count--) {
   115         M3Guint argb = *src++;
   116         M3Guint sum = RGBSUM(argb);
   117         *dst++ = (M3Gubyte)(sum >> 16);
   118     }
   119 }
   120 
   121 /*!
   122  * \internal \brief ARGB -> LA */
   123 static void convertARGBToLA4(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   124 {
   125     while (count--) {
   126         M3Guint argb = *src++;
   127         M3Guint sum = RGBSUM(argb);
   128         *dst++ = (M3Gubyte)(((sum >> 16) & 0xF0) | ((argb >> 28) & 0x0F));
   129     }
   130 }
   131 
   132 /*!
   133  * \internal \brief ARGB -> LA8
   134  */
   135 static void convertARGBToLA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   136 {
   137     while (count--) {
   138         M3Guint argb = *src++;
   139         M3Guint sum = RGBSUM(argb);
   140         *dst++ = (M3Gubyte)(sum >> 16);       /* L */
   141         *dst++ = (M3Gubyte) ALPHA(argb);
   142     }
   143 }
   144 
   145 /*!
   146  * \internal \brief ARGB -> RGB
   147  */
   148 static void convertARGBToRGB8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   149 {
   150     while (count--) {
   151         M3Guint argb = *src++;
   152         *dst++ = (M3Gubyte) RED(argb);
   153         *dst++ = (M3Gubyte) GREEN(argb);
   154         *dst++ = (M3Gubyte) BLUE(argb);
   155     }
   156 }
   157 
   158 /*!
   159  * \internal \brief ARGB -> RGB565
   160  */
   161 static void convertARGBToRGB565(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   162 {
   163     while (count--) {
   164         M3Guint argb = *src++;
   165         *(M3Gushort*)dst = (M3Gushort)(((argb >> 8) & 0xF800u)|
   166                                        ((argb >> 5) & 0x07E0u)|
   167                                        ((argb >> 3) & 0x001Fu));
   168         dst += 2;
   169     }
   170 }
   171 
   172 /*!
   173  * \internal \brief ARGB -> RGBA
   174  */
   175 static void convertARGBToRGBA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   176 {
   177     while (count--) {
   178         M3Guint argb = *src++;
   179         *dst++ = (M3Gubyte) RED(argb);
   180         *dst++ = (M3Gubyte) GREEN(argb);
   181         *dst++ = (M3Gubyte) BLUE(argb);
   182         *dst++ = (M3Gubyte) ALPHA(argb);
   183     }
   184 }
   185 
   186 /*!
   187  * \internal \brief ARGB -> BGRA
   188  */
   189 static void convertARGBToBGRA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   190 {
   191     while (count--) {
   192         M3Guint argb = *src++;
   193         *dst++ = (M3Gubyte) BLUE(argb);
   194         *dst++ = (M3Gubyte) GREEN(argb);
   195         *dst++ = (M3Gubyte) RED(argb);
   196         *dst++ = (M3Gubyte) ALPHA(argb);
   197     }
   198 }
   199 
   200 #undef RED
   201 #undef GREEN
   202 #undef BLUE
   203 #undef ALPHA
   204 
   205 /*!
   206  * \internal
   207  * \brief Converts a span of ARGB pixels to another format
   208  *
   209  * \param src   source pixels
   210  * \param count pixel count
   211  * \param dstFormat destination format
   212  * \param dst destination pixels
   213  */
   214 static void convertFromARGB(const M3Guint *src,
   215                             M3Gsizei count,
   216                             M3GPixelFormat dstFormat,
   217                             M3Gubyte *dst)
   218 {
   219     switch (dstFormat) {
   220     case M3G_L8:
   221         convertARGBToL8(src, count, dst);
   222         break;
   223     case M3G_A8:
   224         convertARGBToA8(src, count, dst);
   225         break;
   226     case M3G_LA4:
   227         convertARGBToLA4(src, count, dst);
   228         break;
   229     case M3G_LA8:
   230         convertARGBToLA8(src, count, dst);
   231         break;
   232     case M3G_RGB8:
   233         convertARGBToRGB8(src, count, dst);
   234         break;
   235     case M3G_RGB565:
   236         convertARGBToRGB565(src, count, dst);
   237         break;
   238     case M3G_RGBA8:
   239     case M3G_RGB8_32:
   240         convertARGBToRGBA8(src, count, dst);
   241         break;
   242     case M3G_BGRA8:
   243     case M3G_BGR8_32:
   244         convertARGBToBGRA8(src, count, dst);
   245         break;
   246     default:
   247         M3G_ASSERT(M3G_FALSE);  /* conversion not supported */
   248     }
   249 }
   250 
   251 /*--------------------------------------------------------------------*/
   252 
   253 /*!
   254  * \internal \brief A8 -> ARGB
   255  */
   256 static void convertA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   257 {
   258     while (count--) {
   259         M3Guint argb = M3G_RGB_MASK;
   260         argb |= ((M3Guint) *src++) << 24;
   261         *dst++ = argb;
   262     }
   263 }
   264 
   265 /*!
   266  * \internal \brief L8 -> ARGB
   267  */
   268 static void convertL8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   269 {
   270     while (count--) {
   271         M3Guint argb = *src++;
   272         argb |= (argb << 8) | (argb << 16);
   273         argb |= M3G_ALPHA_MASK;
   274         *dst++ = argb;
   275     }
   276 }
   277 
   278 /*!
   279  * \internal \brief LA8 -> ARGB
   280  */
   281 static void convertLA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   282 {
   283     while (count--) {
   284         M3Guint argb = *src++;
   285         argb |= (argb << 8) | (argb << 16);
   286         argb |= ((M3Guint) *src++) << 24;
   287         *dst++ = argb;
   288     }
   289 }
   290 
   291 /*!
   292  * \internal \brief RGB8 -> ARGB
   293  */
   294 static void convertRGB8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   295 {
   296     while (count--) {
   297         M3Guint argb = M3G_ALPHA_MASK;
   298         argb |= ((M3Guint)(*src++)) << 16;
   299         argb |= ((M3Guint)(*src++)) <<  8;
   300         argb |=  (M3Guint)(*src++);
   301         *dst++ = argb;
   302     }
   303 }
   304 
   305 /*!
   306  * \internal \brief RGB565 -> ARGB
   307  */
   308 static void convertRGB565ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   309 {
   310     while (count--) {
   311         M3Guint argb = M3G_ALPHA_MASK;
   312         const M3Guint rgb565 = *(const M3Gushort*)src;
   313         argb |= ((rgb565 & 0xF800u) << 8)|((rgb565 & 0xE000u) << 3);
   314         argb |= ((rgb565 & 0x07E0u) << 5)|((rgb565 & 0x0600u) >> 1);
   315         argb |= ((rgb565 & 0x001Fu) << 3)|((rgb565 & 0x001Cu) >> 2);
   316         *dst++ = argb;
   317         src += 2;
   318     }
   319 }
   320 
   321 /*!
   322  * \internal \brief RGBA8 -> ARGB
   323  */
   324 static void convertRGBA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   325 {
   326     while (count--) {
   327         M3Guint argb;
   328         argb  = ((M3Guint)(*src++)) << 16;
   329         argb |= ((M3Guint)(*src++)) <<  8;
   330         argb |=  (M3Guint)(*src++);
   331         argb |= ((M3Guint)(*src++)) << 24;
   332         *dst++ = argb;
   333     }
   334 }
   335 
   336 /*!
   337  * \internal \brief BGRA8 -> ARGB
   338  */
   339 static void convertBGRA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   340 {
   341     while (count--) {
   342         M3Guint argb;
   343         argb  =  (M3Guint)(*src++);
   344         argb |= ((M3Guint)(*src++)) <<  8;
   345         argb |= ((M3Guint)(*src++)) << 16;
   346         argb |= ((M3Guint)(*src++)) << 24;
   347         *dst++ = argb;
   348     }
   349 }
   350 
   351 /*!
   352  * \internal
   353  * \brief Converts a span of pixels to ARGB
   354  *
   355  * \param srcFormat source format
   356  * \param src   source pixels
   357  * \param count pixel count
   358  * \param dst destination pixels
   359  */
   360 static void convertToARGB(M3GPixelFormat srcFormat,
   361                           const M3Gubyte *src,
   362                           M3Gsizei count,
   363                           M3Guint *dst)
   364 {
   365     switch (srcFormat) {
   366     case M3G_A8:
   367         convertA8ToARGB(src, count, dst);
   368         break;
   369     case M3G_L8:
   370         convertL8ToARGB(src, count, dst);
   371         break;
   372     case M3G_LA8:
   373         convertLA8ToARGB(src, count, dst);
   374         break;
   375     case M3G_RGB8:
   376         convertRGB8ToARGB(src, count, dst);
   377         break;
   378     case M3G_RGB565:
   379         convertRGB565ToARGB(src, count, dst);
   380         break;
   381     case M3G_RGBA8:
   382     case M3G_RGB8_32:
   383         convertRGBA8ToARGB(src, count, dst);
   384         break;
   385     case M3G_BGRA8:
   386     case M3G_BGR8_32:
   387         convertBGRA8ToARGB(src, count, dst);
   388         break;
   389     default:
   390         M3G_ASSERT(M3G_FALSE);  /* conversion not supported */
   391     }
   392 }
   393 
   394 /*!
   395  * \internal
   396  * \brief Fast path for BGRA-to-RGBA conversion
   397  */
   398 #if defined (M3G_HW_ARMV6)
   399 __asm void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
   400                                  M3Gsizei width, M3Gsizei height,
   401                                  M3Gubyte *dst)
   402 {
   403 // r0 = *src
   404 // r1 = srcStride
   405 // r2 = width
   406 // r3 = height
   407 // sp[0] = *dst
   408 
   409 		CODE32
   410 
   411 		CMP		r3, #0				// if height = 0, do nothing
   412 		BXEQ	lr
   413 
   414   		STMFD	sp!, {r4-r12, lr} 
   415 
   416 		LDR		r12, [sp, #(10*4)]
   417 		SUB		r1, r1, r2, LSL #2
   418 		MOV		r14, r2
   419 
   420 _fastConvertBGRAToRGBA_outerLoop
   421 		MOVS	r2, r14, ASR #2			// amount of 4x32 bit writes
   422 		BEQ		_fastConvertBGRAToRGBA_tail
   423 
   424 _fastConvertBGRAToRGBA_innerLoop
   425 
   426 		LDMIA	r0!, {r4-r7}		// AARRGGBB
   427 		SUBS	r2, #1
   428 		REV		r4, r4				// BBGGRRAA
   429 		REV		r5, r5
   430 		REV		r6, r6				
   431 		REV		r7, r7
   432 		MOV		r8, r4, ROR #8		// AABBGGRR
   433 		MOV		r9, r5, ROR #8
   434 		MOV		r10, r6, ROR #8		
   435 		MOV		r11, r7, ROR #8
   436 		STMIA	r12!, {r8-r11}
   437 		BNE		_fastConvertBGRAToRGBA_innerLoop
   438 
   439 _fastConvertBGRAToRGBA_tail
   440 		MOV 	r2, r14, ASR #2
   441 		SUBS	r2, r14, r2, LSL #2		// number of remaining writes in the tail
   442 
   443 _fastConvertBGRAToRGBA_tail_loop
   444 
   445 		LDRNE	r4, [r0], #4
   446 		REVNE	r4, r4
   447 		MOVNE	r8, r4, ROR #8
   448 		STRNE	r8, [r12], #4
   449 		SUBNES	r2, #1
   450 		BNE		_fastConvertBGRAToRGBA_tail_loop
   451 
   452 		SUBS	r3, #1
   453 		ADD		r0, r0, r1
   454 		BNE		_fastConvertBGRAToRGBA_outerLoop
   455 
   456 		LDMFD	sp!, {r4-r12, lr}
   457 		BX		lr
   458 
   459 }
   460 #else /* #if defined (M3G_HW_ARMV6) */
   461 static void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
   462                                   M3Gsizei width, M3Gsizei height,
   463                                   M3Gubyte *dst)
   464 {
   465     unsigned int pixel, pixel2;
   466     unsigned int temp;
   467     unsigned int mask = 0x00ff00ff;
   468     int spanwidth = (width >> 1) - 1;
   469     int x, y;
   470     unsigned int *d = (unsigned int *)dst;
   471 
   472     M3G_ASSERT(width > 2);
   473     
   474     for (y = 0; y < height; ++y) {
   475         unsigned int *s = (unsigned int *)(src + y*srcStride);
   476 
   477         pixel = *s++;
   478 
   479         for (x = 0; x < spanwidth; ++x) {
   480             pixel2 = *s++;
   481 
   482             temp   = pixel & mask;          /* 00RR00BB */
   483             pixel  = pixel - temp;          /* AA00GG00 */
   484             pixel  = pixel | (temp << 16);  /* AABBGG00 */
   485             *d++   = pixel | (temp >> 16);  /* AABBGGRR */
   486 
   487             pixel = *s++;
   488 
   489             temp   = pixel2 & mask;          /* 00RR00BB */
   490             pixel2 = pixel2 - temp;          /* AA00GG00 */
   491             pixel2 = pixel2 | (temp << 16);  /* AABBGG00 */
   492             *d++   = pixel2 | (temp >> 16);  /* AABBGGRR */
   493         }
   494         
   495         pixel2 = *s++;
   496         temp   = pixel & mask;          /* 00RR00BB */
   497         pixel  = pixel - temp;          /* AA00GG00 */
   498         pixel  = pixel | (temp << 16);  /* AABBGG00 */
   499         *d++   = pixel | (temp >> 16);  /* AABBGGRR */
   500 
   501         temp   = pixel2 & mask;          /* 00RR00BB */
   502         pixel2 = pixel2 - temp;          /* AA00GG00 */
   503         pixel2 = pixel2 | (temp << 16);  /* AABBGG00 */
   504         *d++   = pixel2 | (temp >> 16);  /* AABBGGRR */
   505     }
   506 }
   507 #endif /* #if defined (M3G_HW_ARMV6) */
   508 
   509 /*--------------------------------------------------------------------*/
   510 
   511 /*!
   512  * \internal
   513  * \brief Maps a logical image format to an internal pixel format
   514  *
   515  * \param imgFormat logical image format
   516  * \param paletted  paletted flag
   517  * \return the internal image pixel format
   518  */
   519 static M3GPixelFormat getInternalFormat(M3GImageFormat imgFormat,
   520                                         M3Gbool paletted)
   521 {
   522     if (paletted) {
   523         switch (imgFormat) {
   524         case M3G_RGB:
   525 #           if defined(M3G_NGL_TEXTURE_API)
   526             return M3G_PALETTE8_RGB8_32;
   527 #           else
   528             return M3G_PALETTE8_RGB8;
   529 #           endif
   530         case M3G_RGBA:
   531             return M3G_PALETTE8_RGBA8;
   532         default:
   533             M3G_ASSERT(M3G_FALSE);
   534             return (M3GPixelFormat)0;
   535         }
   536     }
   537     else {
   538         M3GPixelFormat format = m3gPixelFormat(imgFormat);
   539         
   540 #       if defined(M3G_NGL_TEXTURE_API)
   541         if (format == M3G_RGB8) {
   542             return (M3G_USE_16BIT_TEXTURES) ? M3G_RGB565 : M3G_RGB8_32;
   543         }
   544         if (format == M3G_LA8) {
   545             return M3G_LA4;
   546         }
   547 #       endif
   548         
   549         return format;
   550     }
   551 }
   552 
   553 /*!
   554  * \internal
   555  * \brief Gets the correct pixel format for setting data to an image
   556  */
   557 static M3GPixelFormat m3gInputDataFormat(const Image *img)
   558 {
   559     /* Any of the paletted formats will do for a paletted image, as
   560      * they all have 8-bit indices; we pick PALETTE8_RGBA8 here */
   561     
   562     if (img->flags & M3G_PALETTED) {
   563         return M3G_PALETTE8_RGBA8;
   564     }
   565     
   566     return m3gPixelFormat(img->format);
   567 }
   568 
   569 
   570 /*!
   571  * \internal
   572  * \brief Returns log2(resolution)+1. Assumes that resolution is power of two.
   573  *
   574  * \param w width in pixels
   575  * \param h height in pixels
   576  * \return number of needed mipmap levels
   577  */
   578 static M3Gint m3gGetNumMipmapLevels(M3Gint w, M3Gint h)
   579 {
   580     M3Gint res = (w > h) ? w : h;
   581     M3Gint levels = 0;
   582     while (res > 0) {
   583         ++levels;
   584         res >>= 1;
   585     };
   586     return levels;
   587 }
   588 
   589 /*!
   590  * \internal
   591  * \brief Downsamples an image to half the original size
   592  *
   593  *
   594  * \param format    pixel format
   595  * \param srcPixels source pixels
   596  * \param pw        pointer to width
   597  * \param ph        pointer to height
   598  * \param dstPixels destination pixels
   599  */
   600 static void m3gDownsample(M3GPixelFormat format,
   601                           const M3Gubyte *srcPixels,
   602                           M3Gint *pw, M3Gint *ph,
   603                           M3Gubyte *dstPixels)
   604 {
   605     M3Gint i, j, bpp, pixStride, lineStride;
   606     M3Gint w = *pw, h = *ph;
   607     M3Gubyte *dst;
   608     M3Guint temp[2][SPAN_BUFFER_SIZE/2];
   609 
   610     M3G_ASSERT_PTR(srcPixels);
   611     M3G_ASSERT(w >= 1 && h >= 1);
   612 
   613     bpp = m3gBytesPerPixel(format);
   614     lineStride = (h > 1) ? w * bpp : 0;
   615     pixStride = (w > 1) ? bpp : 0;
   616 
   617     dst = dstPixels;
   618 
   619     /* Iterate over buffer-sized blocks in the image */
   620     
   621     for (j = 0; j < h; j += 2) {
   622         for (i = 0; i < w; i += SPAN_BUFFER_SIZE/2) {
   623             
   624             /* Fill the buffer from the source image */
   625             
   626             const M3Gubyte *src = srcPixels + (j*lineStride + i*pixStride);
   627             M3Gint c = SPAN_BUFFER_SIZE/2;
   628             if (w - i < c) {
   629                 c = w - i;
   630             }
   631             convertToARGB(format, src, c, &temp[0][0]);
   632             convertToARGB(format, src + lineStride, c, &temp[1][0]);
   633             if (w == 1) {
   634                 temp[0][1] = temp[0][0];
   635                 temp[1][1] = temp[1][0];
   636             }
   637             
   638             /* Average the pixels in the buffer */
   639             {
   640 #               define AG_MASK 0xFF00FF00u
   641 #               define RB_MASK 0x00FF00FFu
   642                 
   643                 M3Gint k;
   644                 for (k = 0; k < c; k += 2) {
   645                     M3Guint ag, rb;
   646 
   647                     /* Add two components in parallel */
   648                     
   649                     ag =  ((temp[0][k] & AG_MASK) >> 8)
   650                         + ((temp[1][k] & AG_MASK) >> 8)
   651                         + ((temp[0][k+1] & AG_MASK) >> 8)
   652                         + ((temp[1][k+1] & AG_MASK) >> 8);
   653                         
   654                     rb =  (temp[0][k] & RB_MASK)
   655                         + (temp[1][k] & RB_MASK)
   656                         + (temp[0][k+1] & RB_MASK)
   657                         + (temp[1][k+1] & RB_MASK);
   658 
   659                     /* Shift to divide by 4, adding ½ for rounding */
   660                     
   661                     temp[0][k>>1] = ((((ag + 0x00020002u) << 6) & AG_MASK) |
   662                                      (((rb + 0x00020002u) >> 2) & RB_MASK));
   663                 }
   664                 
   665 #               undef AG_MASK
   666 #               undef RB_MASK
   667             }
   668 
   669             /* Write result to the output buffer */
   670 
   671             convertFromARGB(&temp[0][0], c>>1, format, dst);
   672             dst += (c>>1) * bpp;
   673         }
   674     }
   675 
   676     /* Return output width and height */
   677     
   678     if (w > 1) {
   679         *pw = (w >> 1);
   680     }
   681     if (h > 1) {
   682         *ph = (h >> 1);
   683     }
   684 }
   685 
   686 /*!
   687  * \internal
   688  * \brief Returns the OpenGL minification filter corresponding to M3G
   689  * filtering flags
   690  */
   691 static GLenum m3gGetGLMinFilter(M3Genum levelFilter, M3Genum imageFilter) 
   692 {
   693     static const GLenum minFilter[3][2] = {
   694         GL_LINEAR, GL_NEAREST,
   695         GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_LINEAR,
   696         GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_NEAREST
   697     };
   698 
   699     return minFilter[levelFilter - M3G_FILTER_BASE_LEVEL][imageFilter - M3G_FILTER_LINEAR];
   700 }
   701 
   702 /*----------------------------------------------------------------------
   703  * Internal functions
   704  *--------------------------------------------------------------------*/
   705 
   706 /*!
   707  * \internal
   708  * \brief Converts an internal ARGB color to four GLfixed components
   709  */
   710 static void m3gGLColor(M3Guint argb, GLfixed *dst)
   711 {
   712     GLfixed r, g, b, a;
   713         
   714     r = (GLfixed)((argb & 0x00FF0000u) >> 16);
   715     g = (GLfixed)((argb & 0x0000FF00u) >>  8);
   716     b = (GLfixed)( argb & 0x000000FFu       );
   717     a = (GLfixed)((argb & 0xFF000000u) >> 24);
   718 
   719     dst[0] = ((r << 8) | r) + (r >> 7);
   720     dst[1] = ((g << 8) | g) + (g >> 7);
   721     dst[2] = ((b << 8) | b) + (b >> 7);
   722     dst[3] = ((a << 8) | a) + (a >> 7);
   723 }
   724 
   725 /*!
   726  * \internal
   727  * \brief Binds an image into the current texture unit and sets up
   728  * texture filtering
   729  */
   730 static void m3gBindTextureImage(Image *img, M3Genum levelFilter, M3Genum imageFilter)
   731 {
   732     M3G_ASSERT_GL;
   733     
   734     /* We have no mipmap generation for paletted images, so disable
   735      * mipmapping in that case */
   736     
   737     if (m3gIsInternallyPaletted(img)) {
   738         levelFilter = M3G_FILTER_BASE_LEVEL;
   739     }
   740 
   741     /* Bind the OpenGL texture object, generating mipmaps if
   742      * required */
   743     
   744     m3gBindTextureObject(img, levelFilter != M3G_FILTER_BASE_LEVEL);
   745 
   746     /* Set up OpenGL texture filtering according to our flags */
   747 
   748     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
   749                     (imageFilter == M3G_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
   750     glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
   751                     m3gGetGLMinFilter(levelFilter, imageFilter));
   752     
   753     M3G_ASSERT_GL;
   754 }
   755 
   756 /*!
   757  * \internal
   758  * \brief Maps a logical image format to the matching default pixel
   759  * format
   760  * 
   761  * \param imgFormat logical image format
   762  * \return a one-byte-per-pixel pixel format
   763  */
   764 static M3GPixelFormat m3gPixelFormat(M3GImageFormat imgFormat)
   765 {
   766     switch (imgFormat) {
   767     case M3G_ALPHA:
   768         return M3G_A8;
   769     case M3G_LUMINANCE:
   770         return M3G_L8;
   771     case M3G_LUMINANCE_ALPHA:
   772         return M3G_LA8;
   773     case M3G_RGB:
   774         return M3G_RGB8;
   775     case M3G_RGBA:
   776         return M3G_RGBA8;
   777     default:
   778         M3G_ASSERT(M3G_FALSE);
   779         return M3G_NO_FORMAT;
   780     }
   781 }
   782 
   783 /*!
   784  * \internal
   785  * \brief Returns the number of bytes per pixel in a given pixel format
   786  *
   787  * \param format pixel format
   788  * \return bytes per pixel
   789  */
   790 static M3Gint m3gBytesPerPixel(M3GPixelFormat format)
   791 {
   792     switch (format) {
   793     case M3G_L8:
   794     case M3G_A8:
   795     case M3G_LA4:
   796     case M3G_PALETTE8_RGB8:
   797     case M3G_PALETTE8_RGB8_32:
   798     case M3G_PALETTE8_RGBA8:
   799         return 1;
   800     case M3G_RGB4:
   801     case M3G_RGB565:
   802     case M3G_RGBA4:
   803     case M3G_RGB5A1:
   804     case M3G_LA8:
   805         return 2;
   806     case M3G_RGB8:
   807         return 3;
   808     case M3G_RGBA8:
   809     case M3G_BGRA8:
   810     case M3G_ARGB8:
   811     case M3G_BGR8_32:
   812     case M3G_RGB8_32:
   813         return 4;
   814     default:
   815         M3G_ASSERT(M3G_FALSE);
   816         return 0;
   817     }
   818 }
   819 
   820 /*!
   821  * \internal
   822  * \brief Converts pixels between formats
   823  *
   824  * \note Only a limited subset of source and destination formats may
   825  * be supported; see the \c convert functions in m3g_image.c
   826  *
   827  * \param srcFormat source format
   828  * \param src       source pixels
   829  * \param dstFormat destination format
   830  * \param dst       destination pixels
   831  * \param count     pixel count
   832  */
   833 static void m3gConvertPixels(M3GPixelFormat srcFormat, const M3Gubyte *src,
   834                              M3GPixelFormat dstFormat, M3Gubyte *dst,
   835                              M3Gsizei count)
   836 {
   837     M3Guint temp[SPAN_BUFFER_SIZE];
   838     const char endianTest[4] = { 1, 0, 0, 0 };
   839 
   840     M3Guint srcBpp = m3gBytesPerPixel(srcFormat);
   841     M3Guint dstBpp = m3gBytesPerPixel(dstFormat);
   842     M3G_ASSERT(srcBpp > 0 && dstBpp > 0);
   843 
   844     while (count > 0) {
   845         M3Gsizei n = count;
   846 
   847         /* Check the source and destination formats to avoid 
   848            the intermediate ARGB format conversion. */
   849         if (((srcFormat == M3G_RGBA8 && (dstFormat == M3G_BGRA8 || dstFormat == M3G_BGR8_32))
   850             || (dstFormat == M3G_RGBA8 && (srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32))) 
   851             && (n > 2) && ((*(const int *)endianTest) == 1)) {
   852             /* use fast path for RGBA<->BGRA conversion */
   853             fastConvertBGRAToRGBA(src, n * srcBpp, n, 1, dst);
   854         } else if (srcFormat == M3G_ARGB8 && dstFormat != M3G_ARGB8) {
   855             convertFromARGB((M3Guint*)src, n, dstFormat, dst);
   856         } else if (srcFormat != M3G_ARGB8 && dstFormat == M3G_ARGB8) {
   857             convertToARGB(srcFormat, src, n, (M3Guint*)dst);
   858         } else {
   859             /* no luck, do the conversion via ARGB (source format -> ARGB -> destination format) */
   860             n = (count < SPAN_BUFFER_SIZE) ? count : SPAN_BUFFER_SIZE;
   861             convertToARGB(srcFormat, src, n, temp);
   862             convertFromARGB(temp, n, dstFormat, dst);
   863         }
   864         count -= n;
   865         src += n * srcBpp;
   866         dst += n * dstBpp;
   867     }
   868 }
   869 
   870 /*!
   871  * \internal
   872  * \brief Copies image data. The source image is copied to
   873  * the destination image.
   874  *
   875  * \param dst destination image
   876  * \param src source image
   877  */
   878 static void m3gCopyImagePixels(Image *dst,
   879                                const Image *src)
   880 {
   881     const M3Gubyte *pSrc;
   882     M3Gubyte *pDst;
   883     M3Gint bpp;
   884 
   885     /* Check inputs (debug only!) */
   886     M3G_VALIDATE_OBJECT(dst);
   887     M3G_VALIDATE_OBJECT(src);
   888 
   889     M3G_ASSERT(src->internalFormat == dst->internalFormat);
   890     M3G_ASSERT(src->format == dst->format);
   891 
   892     M3G_ASSERT(src->paletteBytes == dst->paletteBytes);
   893     
   894     /* Compute source and destination pixel data pointers */
   895     pSrc = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(src), src->data);
   896     pDst = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(dst), dst->data);
   897 
   898     bpp = m3gBytesPerPixel(src->internalFormat);
   899 
   900     if (src->paletteBytes > 0) {
   901         m3gCopy(pDst, pSrc, src->paletteBytes);
   902         pDst += dst->paletteBytes;
   903         pSrc += src->paletteBytes;
   904     }
   905 
   906     /* Do a straight copy if the sizes match, or resample if not */
   907     if (src->width == dst->width && src->height == dst->height ) {
   908         m3gCopy(pDst, pSrc, src->width * src->height * bpp);
   909     }
   910     else {
   911         /* Adder values as 8.8 fixed point */
   912         M3Gint xAdd, yAdd;
   913         M3Gint x, y;
   914 
   915         xAdd = (256 * src->width) / dst->width;
   916         yAdd = (256 * src->height) / dst->height;
   917 
   918         for (y = 0; y < dst->height; y++) {
   919             for (x = 0; x < dst->width; x++) {
   920                 m3gCopy(pDst, pSrc + bpp * (((xAdd * x) >> 8) + ((yAdd * y) >> 8) * src->width), bpp);
   921                 pDst += bpp;
   922             }
   923         }
   924     }
   925 
   926     m3gUnmapObject(M3G_INTERFACE(dst), dst->data);
   927     m3gUnmapObject(M3G_INTERFACE(src), src->data);
   928 
   929     m3gInvalidateImage(dst);
   930 }
   931 
   932 /*!
   933  * \internal
   934  * \brief Invalidates any cached data for this image
   935  *
   936  * Used when rendering to the image.
   937  *
   938  * \param img Image object
   939  */
   940 static void m3gInvalidateImage(Image *img)
   941 {
   942     M3G_VALIDATE_OBJECT(img);
   943     img->dirty = M3G_TRUE;
   944     
   945 #   if !defined(M3G_NGL_TEXTURE_API)
   946     if (img->large) {
   947         img->large->dirty = M3G_TRUE;
   948     }
   949 #   endif /*M3G_NGL_TEXTURE_API*/
   950 
   951     if (img->powerOfTwo != img) {
   952         img->powerOfTwoDirty = M3G_TRUE;
   953     }
   954 }
   955 
   956 /*!
   957  * \internal
   958  * \brief Overloaded Object3D method.
   959  *
   960  * \param originalObj original Image object
   961  * \param cloneObj pointer to cloned Image object
   962  * \param pairs array for all object-duplicate pairs
   963  * \param numPairs number of pairs
   964  */
   965 static M3Gbool m3gImageDuplicate(const Object *originalObj,
   966                                  Object **cloneObj,
   967                                  Object **pairs,
   968                                  M3Gint *numPairs)
   969 {
   970     Image *original = (Image *)originalObj;
   971     Image *clone;
   972 
   973     /* If the original image still has its pixel data, make a full
   974      * copy -- this is wasteful for immutable images, but the shame's
   975      * on the user in that case */
   976     
   977     if (original->data) {
   978         clone = (Image*) m3gCreateImage(originalObj->interface,
   979                                         original->format,
   980                                         original->width,
   981                                         original->height,
   982                                         original->flags);
   983     }
   984     else {
   985 
   986         /* Otherwise, just point to the original and use its data
   987          * buffers */
   988         
   989         clone = (Image*) m3gAlloc(M3G_INTERFACE(original), sizeof(*clone));
   990         *clone = *original;
   991         M3G_ASSIGN_REF(clone->copyOf, original);
   992     }
   993     
   994     *cloneObj = (Object *)clone;
   995     if (*cloneObj == NULL) {
   996         return M3G_FALSE;
   997     }
   998 
   999     if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
  1000         /* Copy image contents */
  1001         if (original->data) {
  1002             m3gCopyImagePixels(clone, original);
  1003         }
  1004         return M3G_TRUE;
  1005     }
  1006     else {
  1007         return M3G_FALSE;
  1008     }
  1009 }
  1010 
  1011 /*!
  1012  * \internal
  1013  *
  1014  * \brief Frees the pixel data associated with this image; used for
  1015  * optimizing memory usage after copying the data to a secondary
  1016  * location
  1017  */
  1018 static void m3gFreeImageData(Image *img)
  1019 {
  1020     M3G_ASSERT(img->format == M3G_RGB || img->format == M3G_LUMINANCE);
  1021 #   if !defined(M3G_NGL_TEXTURE_API)
  1022     M3G_ASSERT(!img->mipmapsDirty);
  1023 #   endif
  1024     M3G_ASSERT(!img->powerOfTwoDirty);
  1025     M3G_ASSERT(img->powerOfTwo != NULL);
  1026     M3G_ASSERT(!img->pinned);
  1027     
  1028     M3G_LOG1(M3G_LOG_IMAGES, "Freeing copy of image 0x%08X\n",
  1029              (unsigned) img);
  1030 
  1031     if (!img->copyOf) {
  1032         m3gFreeObject(M3G_INTERFACE(img), img->data);
  1033         img->data = 0;
  1034         m3gFreeObject(M3G_INTERFACE(img), img->mipData);
  1035         img->mipData = 0;
  1036     }
  1037     M3G_ASSIGN_REF(img->copyOf, NULL);
  1038 }
  1039 
  1040 /*!
  1041  * \internal
  1042  * \brief Returns a power-of-two variant of an image
  1043  *
  1044  * This is used for sprites and background images.
  1045  */
  1046 static Image *m3gGetPowerOfTwoImage(Image *img)
  1047 {
  1048     M3G_VALIDATE_OBJECT(img);
  1049     
  1050     /* Create a power-of-two variant of the image if one doesn't exist
  1051      * already */
  1052     
  1053     if (img->powerOfTwo == NULL) {
  1054 
  1055         M3Gint width, height;
  1056         M3Gbitmask flags;
  1057         Image *potImage;
  1058         
  1059         M3G_ASSERT(!m3gIsPowerOfTwo(img->width) ||
  1060                    !m3gIsPowerOfTwo(img->height));
  1061         
  1062         /* Choose new image size to allow a maximum shrinkage of 25%;
  1063          * this is to weed out pathological cases of quadruple memory
  1064          * usage because an image is one pixel too wide */
  1065 
  1066         width  = m3gNextPowerOfTwo((img->width * 3) >> 2);
  1067         height = m3gNextPowerOfTwo((img->height * 3) >> 2);
  1068         
  1069         width  = M3G_MIN(width, M3G_MAX_TEXTURE_DIMENSION);
  1070         height = M3G_MIN(height, M3G_MAX_TEXTURE_DIMENSION);
  1071         
  1072         flags = img->flags & (~M3G_RENDERING_TARGET);
  1073         
  1074         potImage = m3gCreateImage(M3G_INTERFACE(img),
  1075                                   img->format,
  1076                                   width, height,
  1077                                   flags);
  1078         if (!potImage) {
  1079             return NULL; /* automatic out-of-memory */
  1080         }
  1081 
  1082         M3G_ASSIGN_REF(img->powerOfTwo, potImage);
  1083         img->powerOfTwoDirty = M3G_TRUE;
  1084     }
  1085 
  1086     /* Update POT image data if necessary */
  1087     
  1088     if (img->powerOfTwoDirty) {
  1089         m3gCopyImagePixels(img->powerOfTwo, img);
  1090         img->powerOfTwoDirty = M3G_FALSE;
  1091 
  1092         /* Get rid of the original at this point if we can */
  1093         
  1094         if (!img->pinned) {
  1095             m3gFreeImageData(img);
  1096         }
  1097     }
  1098 
  1099     return img->powerOfTwo;
  1100 }
  1101 
  1102 /*!
  1103  * \internal
  1104  * \brief Gets image alpha at x, y.
  1105  *
  1106  * \param image Image object
  1107  * \param x x-coordinate
  1108  * \param y y-coordinate
  1109  * \return alpha value
  1110  *
  1111  */
  1112 static M3Gint m3gGetAlpha(Image *image, M3Gint x, M3Gint y)
  1113 {
  1114     M3Gint alpha = 255;
  1115     M3Gint bpp = m3gBytesPerPixel(image->internalFormat);
  1116     M3Guint data = 0;
  1117     M3Gubyte *pixels;
  1118 
  1119     /* Quick exit for non-alpha formats */
  1120     
  1121     if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
  1122         return alpha;
  1123     }
  1124 
  1125     /* For other formats, we have to sample the image data */
  1126 
  1127     if (!image->data) {
  1128         Image *potImage = image->powerOfTwo;
  1129         M3G_ASSERT(potImage != image);
  1130         return m3gGetAlpha(potImage,
  1131                            (x * image->width) / potImage->width,
  1132                            (y * image->height) / potImage->height);
  1133     }
  1134     
  1135     pixels = ((M3Gubyte *)m3gMapObject(M3G_INTERFACE(image), image->data));
  1136 
  1137     if (image->paletteBytes == 0) {
  1138         if (bpp == 1) {
  1139             data = pixels[x + y * image->width];
  1140         }
  1141         else if (bpp == 2) {
  1142             data = ((M3Gushort *)pixels)[x + y * image->width];
  1143         }
  1144         else {
  1145             data = ((M3Guint *)pixels)[x + y * image->width];
  1146         }
  1147     }
  1148     else {
  1149         M3Guint *palette;
  1150         palette = (M3Guint *)pixels;
  1151         pixels += image->paletteBytes;
  1152 
  1153         data = palette[pixels[x + y * image->width]];
  1154     }
  1155 
  1156     m3gUnmapObject(M3G_INTERFACE(image), image->data);
  1157 
  1158     switch (image->internalFormat) {
  1159 
  1160     case M3G_A8:
  1161         alpha = data;
  1162         break;
  1163     case M3G_LA8:
  1164         alpha = data >> 8;
  1165         break;
  1166     case M3G_RGBA8:
  1167         alpha = data >> 24;
  1168         break;
  1169     default:
  1170         /* Should never be here!! */
  1171         M3G_ASSERT(M3G_FALSE);
  1172     }
  1173 
  1174     return alpha;
  1175 }
  1176 
  1177 /*!
  1178  * \internal
  1179  * \brief Computes the scanline stride of an image
  1180  */
  1181 static M3Gsizei m3gGetImageStride(const Image *img)
  1182 {
  1183     M3G_VALIDATE_OBJECT(img);
  1184     return img->width * m3gBytesPerPixel(img->internalFormat);
  1185 }
  1186 
  1187 /*----------------------------------------------------------------------
  1188  * Virtual function table
  1189  *--------------------------------------------------------------------*/
  1190 
  1191 static const ObjectVFTable m3gvf_Image = {
  1192     m3gObjectApplyAnimation,
  1193     m3gObjectIsCompatible,
  1194     m3gObjectUpdateProperty,
  1195     m3gObjectDoGetReferences,
  1196     m3gObjectFindID,
  1197     m3gImageDuplicate,
  1198     m3gDestroyImage
  1199 };
  1200 
  1201 
  1202 /*----------------------------------------------------------------------
  1203  * Public API functions
  1204  *--------------------------------------------------------------------*/
  1205 
  1206 /*!
  1207  * \brief Creates a new Image
  1208  *
  1209  * \param interface     M3G interface
  1210  * \param srcFormat     source format
  1211  * \param width         width in pixels
  1212  * \param height        height in pixels
  1213  * \param flags         creation flags; a combination of
  1214  *                      M3G_DYNAMIC, M3G_STATIC,
  1215  *                      M3G_RENDERING_TARGET, and M3G_PALETTED
  1216  * \retval Image new Image object
  1217  * \retval NULL Image creating failed
  1218  */
  1219 M3G_API M3GImage m3gCreateImage(/*@dependent@*/ M3GInterface interface,
  1220                                 M3GImageFormat srcFormat,
  1221                                 M3Gint width, M3Gint height,
  1222                                 M3Gbitmask flags)
  1223 {
  1224     Interface *m3g = (Interface *) interface;
  1225     M3G_VALIDATE_INTERFACE(m3g);
  1226     
  1227     /* Check errors */
  1228     
  1229     if (width <= 0 || height <= 0) {
  1230         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1231         return NULL;
  1232     }
  1233 
  1234     if (!m3gInRange(srcFormat, M3G_ALPHA, M3G_RGBA)) {
  1235         m3gRaiseError(m3g, M3G_INVALID_ENUM);
  1236         return NULL;
  1237     }
  1238 
  1239     /* Parameters OK; allocate and initialize the object */
  1240 
  1241     {
  1242         Image *img = m3gAllocZ(m3g, sizeof(Image));
  1243         if (img == NULL) {
  1244             return NULL;
  1245         }
  1246 
  1247         /* Clean up and set flags */
  1248 
  1249         M3G_LOG3(M3G_LOG_IMAGES, "Image 0x%08X is %d x %d",
  1250                  (unsigned) img, width, height);
  1251         
  1252         flags |= M3G_DYNAMIC;   /* the default */
  1253         
  1254         if (flags & M3G_STATIC) {
  1255             M3G_LOG(M3G_LOG_IMAGES, ", immutable");
  1256             flags &= ~M3G_DYNAMIC;
  1257         }
  1258         if (flags & M3G_RENDERING_TARGET) {
  1259             M3G_LOG(M3G_LOG_IMAGES, ", rendertarget");
  1260             flags |= M3G_DYNAMIC;
  1261         }
  1262         if (flags & M3G_PALETTED) {
  1263             M3G_LOG(M3G_LOG_IMAGES, ", paletted");
  1264         }
  1265         img->flags = flags;
  1266 
  1267         M3G_LOG(M3G_LOG_IMAGES, "\n");
  1268         
  1269         {
  1270             /* Allocate pixel & palette data; the palette is stored at
  1271              * the beginning of the pixel data chunk */
  1272 
  1273             M3Gbool paletted = ((img->flags & M3G_PALETTED) != 0)
  1274                 && m3gSupportedPaletteFormat(srcFormat);
  1275             M3GPixelFormat internalFormat = getInternalFormat(srcFormat,
  1276                                                               paletted);
  1277             M3Guint bpp = m3gBytesPerPixel(internalFormat);
  1278             M3Guint pixelBytes = width * height * bpp;
  1279 
  1280             if ((img->flags & M3G_PALETTED) != 0 && !paletted) {
  1281                 M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_IMAGES,
  1282                         "Warning: Unsupported paletted format\n");
  1283             }
  1284                 
  1285             /* The palette will always have 256 elements and one byte
  1286              * per color component (except padded 32-bit for NGL) */
  1287             
  1288             if (paletted) {
  1289                 img->paletteBytes =
  1290 #                   if defined(M3G_NGL_TEXTURE_API)
  1291                     256 * 4;
  1292 #                   else
  1293                     256 * m3gBytesPerPixel(m3gPixelFormat(srcFormat));
  1294 #                   endif
  1295             }
  1296 
  1297             /* Set up the rest of the image parameters */
  1298             
  1299             img->width = width;
  1300             img->height = height;
  1301             img->format = srcFormat;
  1302             img->internalFormat = internalFormat;
  1303             img->glFormat = m3gGetGLFormat(internalFormat);
  1304 
  1305             M3G_LOG1(M3G_LOG_IMAGES, "Image data %d bytes\n",
  1306                      pixelBytes + img->paletteBytes);
  1307             
  1308             /* Allocate the image memory */
  1309             
  1310             img->data = m3gAllocObject(m3g, pixelBytes + img->paletteBytes);
  1311             if (img->data == 0) {
  1312                 m3gFree(m3g, img);
  1313                 return NULL;
  1314             }
  1315 
  1316 #ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
  1317             /* If GLES resource freeing (see function m3gFreeGLESResources) 
  1318                is enabled, the GL texture might get deleted at any point, so
  1319 			   a copy of the texture data has to be always kept in memory. */
  1320             img->pinned = M3G_TRUE;
  1321 #else           
  1322             /* Lock the image data in memory if the image is dynamic,
  1323              * or the format has alpha information; otherwise, we'll
  1324              * be able to get rid of an extra copy when generating a
  1325              * power-of-two version or uploading to OpenGL */
  1326             
  1327             if ((img->flags & M3G_DYNAMIC) != 0
  1328                 || (img->format != M3G_RGB &&
  1329                     img->format != M3G_LUMINANCE)) {
  1330                 img->pinned = M3G_TRUE;
  1331             }
  1332 #endif
  1333             /* If the image can be used as a rendering target, clear
  1334              * to opaque white by default */
  1335             
  1336             if ((img->flags & M3G_RENDERING_TARGET) != 0) {
  1337                 M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data))
  1338                     + img->paletteBytes;
  1339                 m3gFill(pixels, (size_t) pixelBytes, -1); 
  1340                 m3gUnmapObject(m3g, img->data);
  1341             }
  1342 
  1343             /* Check for "special" images that can't be used as
  1344              * textures without some extra trickery */
  1345 
  1346             if (!m3gIsPowerOfTwo((M3Guint) width) ||
  1347                 !m3gIsPowerOfTwo((M3Guint) height)) {
  1348                 img->special |= IMG_NPOT;
  1349             }
  1350             else {
  1351                 img->powerOfTwo = img;
  1352             }
  1353             
  1354             if (width > M3G_MAX_TEXTURE_DIMENSION ||
  1355                 height > M3G_MAX_TEXTURE_DIMENSION) {
  1356                 img->special |= IMG_LARGE;
  1357             }
  1358         }
  1359 
  1360         /* Call base class constructor (can not fail) and return */
  1361         m3gInitObject(&img->object, m3g, M3G_CLASS_IMAGE);
  1362 
  1363         M3G_VALIDATE_OBJECT(img);
  1364         return (M3GImage) img;
  1365     }
  1366 }
  1367 
  1368 /*!
  1369  * \brief Prevents further modifications to an image
  1370  *
  1371  * Essentially, this changes the default M3G_DYNAMIC flag to
  1372  * M3G_STATIC; this allows the implementation to make memory and
  1373  * performance optimizations not possible for dynamically modified
  1374  * images.
  1375  */
  1376 M3G_API void m3gCommitImage(M3GImage hImage)
  1377 {
  1378     Image *image = (Image *) hImage;
  1379     M3Gbitmask flags;
  1380     M3G_VALIDATE_OBJECT(image);
  1381     
  1382     flags = image->flags;
  1383     flags &= ~(M3G_DYNAMIC|M3G_RENDERING_TARGET);
  1384     flags |= M3G_STATIC;
  1385     
  1386     image->flags = flags;
  1387 
  1388 #ifndef M3G_ENABLE_GLES_RESOURCE_HANDLING
  1389     /* If the image format has no alpha information, we can discard
  1390      * the image data under suitable conditions */
  1391     
  1392     if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
  1393         image->pinned = M3G_FALSE;
  1394     }
  1395 #endif    
  1396     M3G_LOG1(M3G_LOG_IMAGES, "Image 0x%08X made immutable\n",
  1397              (unsigned) image);
  1398 }
  1399 
  1400 /*!
  1401  * \brief Check if image is mutable.
  1402  * 
  1403  * \param hImage Image object
  1404  * \retval M3G_TRUE image is mutable
  1405  * \retval M3G_FALSE image is immutable
  1406  */
  1407 M3G_API M3Gbool m3gIsMutable(M3GImage hImage)
  1408 {
  1409     Image *image = (Image *) hImage;
  1410     M3G_VALIDATE_OBJECT(image);
  1411     return ((image->flags & M3G_DYNAMIC) != 0);
  1412 }
  1413 
  1414 /*!
  1415  * \brief Gets image format as JSR-184 constant
  1416  * 
  1417  * \param hImage Image object
  1418  * \return JSR-184 format
  1419  */
  1420 M3G_API M3GImageFormat m3gGetFormat(M3GImage hImage)
  1421 {
  1422     Image *image = (Image *) hImage;
  1423     M3G_VALIDATE_OBJECT(image);
  1424     return image->format;
  1425 }
  1426 
  1427 /*!
  1428  * \brief Gets image width
  1429  * 
  1430  * \param hImage Image object
  1431  * \return width in pixels
  1432  */
  1433 M3G_API M3Gint m3gGetWidth(M3GImage hImage)
  1434 {
  1435     Image *image = (Image *) hImage;
  1436     M3G_VALIDATE_OBJECT(image);
  1437     return image->width;
  1438 }
  1439 
  1440 /*!
  1441  * \brief Gets image height
  1442  * 
  1443  * \param hImage Image object
  1444  * \return height in pixels
  1445  */
  1446 M3G_API M3Gint m3gGetHeight(M3GImage hImage)
  1447 {
  1448     Image *image = (Image *) hImage;
  1449     M3G_VALIDATE_OBJECT(image);
  1450     return image->height;
  1451 }
  1452 
  1453 /*!
  1454  * \brief Converts a rectangle of pixels of src to dst as srcFormat to
  1455  * dstFormat conversion requires.
  1456  *
  1457  * \param srcFormat source format
  1458  * \param src       source pixels
  1459  * \param srcStride source stride
  1460  * \param width     width in pixels
  1461  * \param height    height in pixels
  1462  * \param dstFormat destination format
  1463  * \param dst       destination pixels
  1464  * \param dstStride destination stride
  1465  */
  1466 static void m3gConvertPixelRect(
  1467     M3GPixelFormat srcFormat, const M3Gubyte *src, M3Gsizei srcStride,
  1468     M3Gsizei width, M3Gsizei height,
  1469     M3GPixelFormat dstFormat, M3Gubyte *dst, M3Gsizei dstStride)
  1470 {
  1471     /* Detect any fast path cases */
  1472     
  1473     if ((srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32)
  1474         && dstFormat == M3G_RGBA8) {
  1475         if (width > 2 && dstStride == width*4) {
  1476 
  1477             const char endianTest[4] = { 1, 0, 0, 0 };
  1478             if ((*(const int *)endianTest) == 1) {
  1479                 fastConvertBGRAToRGBA(src, srcStride, width, height, dst);
  1480             }
  1481             return;
  1482         }
  1483     }
  1484 
  1485     /* No luck, do the generic conversion */
  1486         
  1487     while (height-- > 0) {
  1488         m3gConvertPixels(srcFormat, src, dstFormat, dst, width);
  1489         src += srcStride;
  1490         dst += dstStride;
  1491     }
  1492 }
  1493 
  1494 /*!
  1495  * \brief Sets the pixel data for an image
  1496  * 
  1497  * \param hImage Image object
  1498  * \param srcPixels source pixels
  1499  */
  1500 M3G_API void m3gSetImage(M3GImage hImage, const void *srcPixels)
  1501 {
  1502     Image *img = (Image *) hImage;
  1503     M3G_VALIDATE_OBJECT(img);
  1504 
  1505     {
  1506         M3Gsizei bpp = m3gBytesPerPixel(m3gInputDataFormat(img));
  1507         m3gSetSubImage(hImage,
  1508                        0, 0, img->width, img->height,
  1509                        img->width * img->height * bpp, srcPixels);
  1510     }
  1511 }
  1512 
  1513 /*!
  1514  * \brief Reads pixel data from an image
  1515  *
  1516  * \param hImage Image object
  1517  * \param pixels output buffer for pixels
  1518  */
  1519 M3G_API void m3gGetImageARGB(M3GImage hImage, M3Guint *pixels)
  1520 {
  1521     Interface *m3g;
  1522     const Image *img = (const Image *) hImage;
  1523     M3G_VALIDATE_OBJECT(img);
  1524     m3g = M3G_INTERFACE(img);
  1525     
  1526     if (!pixels) {
  1527         m3gRaiseError(m3g, M3G_NULL_POINTER);
  1528         return;
  1529     }
  1530     
  1531     if (img->data) {
  1532         const M3Gubyte *src = (const M3Gubyte*) m3gMapObject(m3g, img->data);
  1533         convertToARGB(img->internalFormat, src,
  1534                       img->width * img->height,
  1535                       pixels);
  1536         m3gUnmapObject(m3g, img->data);
  1537     }
  1538 }
  1539 
  1540 /*!
  1541  * \brief Sets the palette for an image
  1542  * 
  1543  * \param hImage Image object
  1544  * \param paletteLength length of the palette
  1545  * \param srcPalette palette data
  1546  */
  1547 M3G_API void m3gSetImagePalette(M3GImage hImage,
  1548                                 M3Gint paletteLength,
  1549                                 const void *srcPalette)
  1550 {
  1551     Interface *m3g;
  1552     Image *img = (Image *) hImage;
  1553     M3G_VALIDATE_OBJECT(img);
  1554     m3g = M3G_INTERFACE(img);
  1555 
  1556     /* Check for errors */
  1557 
  1558     if (img->data == 0 || (img->flags & M3G_STATIC) != 0
  1559             || (img->flags & M3G_PALETTED) == 0) {
  1560         M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
  1561         m3gRaiseError(m3g, M3G_INVALID_OPERATION);
  1562         return;
  1563     }
  1564     if (srcPalette == NULL) {
  1565         m3gRaiseError(m3g, M3G_NULL_POINTER);
  1566         return;
  1567     }
  1568     if (!m3gInRange(paletteLength, 0, 256)) {
  1569         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1570         return;
  1571     }
  1572 
  1573     /*
  1574      * Copy the palette data into the allocated palette (for natively
  1575      * supported paletted formats), or remap the existing image data
  1576      * using the supplied palette entries (for non-native formats)
  1577      *
  1578      * NOTE the latter is a one-time operation!
  1579      */
  1580     if (img->paletteBytes > 0) {
  1581         M3Gubyte *palette = (M3Gubyte *)m3gMapObject(m3g, img->data);
  1582 #       if defined(M3G_NGL_TEXTURE_API)
  1583         m3gConvertPixels(m3gPixelFormat(img->format), srcPalette,
  1584                          M3G_RGBA8, palette,
  1585                          paletteLength);
  1586 #       else
  1587         M3Gsizei bpp = m3gBytesPerPixel(m3gPixelFormat(img->format));
  1588         m3gCopy(palette, srcPalette, (size_t) paletteLength * bpp);
  1589 #       endif
  1590     }
  1591     else {
  1592         M3Gint count = img->width * img->height;
  1593         M3Gubyte *pixel = (M3Gubyte*)m3gMapObject(m3g, img->data);
  1594         const M3Gubyte *bytePalette = (const M3Gubyte *) srcPalette;
  1595 
  1596         /* We need to treat the input and internal formats as
  1597          * separate, as the internal storage may be padded to more
  1598          * bytes than there are color components */
  1599 
  1600         M3GPixelFormat paletteFormat = m3gPixelFormat(img->format);
  1601         const int numComponents = m3gBytesPerPixel(paletteFormat);
  1602         M3GPixelFormat imgFormat = img->internalFormat;
  1603         const int imgBpp = m3gBytesPerPixel(imgFormat);
  1604 
  1605         /* In most cases we can just copy the corresponding palette
  1606          * entry on top of each pixel based on the pixel intensity (R
  1607          * or L component), but special formats require a more
  1608          * complicated conversion.  We just use the (slow) general
  1609          * conversion routine, as it already incorporates support for
  1610          * all formats. */
  1611         
  1612         if (imgBpp >= numComponents) {
  1613             while (count--) {
  1614                 int offset = (*pixel) * numComponents;
  1615                 int c;
  1616                 for (c = 0; c < numComponents; ++c) {
  1617                     *pixel++ = bytePalette[offset + c];
  1618                 }
  1619                 while (c++ < imgBpp) { /* padding for e.g. 24-bit RGB */
  1620                     *pixel++ = 0xFF;
  1621                 }
  1622             }
  1623         }
  1624         else {
  1625             while (count--) {
  1626                 int offset = (*pixel) * numComponents;
  1627                 m3gConvertPixels(paletteFormat, &bytePalette[offset],
  1628                                  imgFormat, pixel,
  1629                                  1);
  1630                 pixel += imgBpp;
  1631             }
  1632         }
  1633     }
  1634     m3gUnmapObject(m3g, img->data);
  1635     m3gInvalidateImage(img);
  1636 }
  1637 
  1638 /*!
  1639  * \brief Sets a scanline of an image
  1640  * 
  1641  * \param hImage Image object
  1642  * \param line scanline
  1643  * \param trueAlpha M3G_TRUE if the source image has an alpha channel,
  1644  *                  M3G_FALSE if it should come from the RGB values;
  1645  *                  this only matters for alpha-only destination images
  1646  * \param pixels souce pixels
  1647  */
  1648 M3G_API void m3gSetImageScanline(M3GImage hImage,
  1649                                  M3Gint line,
  1650                                  M3Gbool trueAlpha,
  1651                                  const M3Guint *pixels)
  1652 {
  1653     Image *img = (Image *) hImage;
  1654     M3G_VALIDATE_OBJECT(img);
  1655 
  1656     if (img->data == 0 || (img->flags & M3G_STATIC) != 0
  1657             || img->paletteBytes != 0) {
  1658         m3gRaiseError(M3G_INTERFACE(img), M3G_INVALID_OPERATION);
  1659         return;
  1660     }
  1661     
  1662     {
  1663         Interface *m3g = M3G_INTERFACE(img);
  1664         M3Gint stride = img->width * m3gBytesPerPixel(img->internalFormat);
  1665         M3Gubyte *dst = ((M3Gubyte *) m3gMapObject(m3g, img->data))
  1666             + img->paletteBytes;
  1667 
  1668 #ifdef M3G_NGL_TEXTURE_API
  1669         /* For RGB images without alpha channel, source alpha is
  1670          * forced to 0xff. */
  1671 
  1672         if (img->format == M3G_RGB) {
  1673             M3Gint i;
  1674             M3Guint argb, *dst;
  1675 
  1676             dst = (M3Guint *) pixels;
  1677 
  1678             for (i = 0; i < img->width; i++) {
  1679                 argb = *dst | 0xff000000;
  1680                 *dst++ = argb;
  1681             }
  1682         }
  1683 #endif
  1684 
  1685         /* Note that an alpha-only destination format is faked for
  1686          * luminance if the source contained no true alpha data; alpha
  1687          * is then inferred from the RGB values instead */
  1688         
  1689         convertFromARGB(pixels,
  1690                         img->width,
  1691                         (img->internalFormat == M3G_A8 && !trueAlpha) ? M3G_L8 : img->internalFormat,
  1692                         dst + line * stride);
  1693 
  1694         m3gUnmapObject(m3g, img->data);
  1695         m3gInvalidateImage(img);
  1696     }
  1697 }
  1698 
  1699 /*!
  1700  * \brief Sets a rectangular subregion of an image
  1701  * 
  1702  * \param hImage Image object
  1703  * \param x x-coordinate in destination image
  1704  * \param y y-coordinate in destination image
  1705  * \param width width of source pixels
  1706  * \param height height of source pixels
  1707  * \param length length of source data, in bytes
  1708  * \param pixels source pixels
  1709  */
  1710 M3G_API void m3gSetSubImage(M3GImage hImage,
  1711                             M3Gint x, M3Gint y,
  1712                             M3Gint width, M3Gint height,
  1713                             M3Gint length, const void *pixels)
  1714 {
  1715     Interface *m3g;
  1716     Image *img = (Image *) hImage;
  1717 
  1718     M3GPixelFormat srcFormat;
  1719     M3Gsizei srcBpp;
  1720 
  1721     M3G_VALIDATE_OBJECT(img);
  1722     m3g = M3G_INTERFACE(img);
  1723 
  1724     /* Check for errors */
  1725 
  1726     if (img->data == 0 || (img->flags & M3G_STATIC) != 0) {
  1727         M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
  1728         m3gRaiseError(m3g, M3G_INVALID_OPERATION);
  1729         return;
  1730     }
  1731     if (pixels == NULL) {
  1732         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1733         return;
  1734     }
  1735     if (x < 0 || y < 0 || width <= 0 || height <= 0
  1736             || x+width > img->width || y+height > img->height) {
  1737         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1738         return;
  1739     }
  1740     
  1741     srcFormat = m3gInputDataFormat(img);
  1742     srcBpp = m3gBytesPerPixel(srcFormat);
  1743     
  1744     if (length < width * height * srcBpp) {
  1745         m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1746         return;
  1747     }    
  1748 
  1749     /* Copy the image data, doing a conversion if the input format
  1750      * does not match the internal storage format */
  1751     {
  1752         const M3Gubyte *srcPixels = (const M3Gubyte*) pixels;
  1753         M3Gsizei srcStride = width * srcBpp;
  1754 
  1755         M3GPixelFormat dstFormat = img->internalFormat;
  1756         M3Gsizei dstBpp = m3gBytesPerPixel(dstFormat);
  1757         M3Gsizei dstStride = img->width * dstBpp;
  1758         M3Gubyte *dstPixels =
  1759             ((M3Gubyte *)m3gMapObject(m3g, img->data))
  1760             + img->paletteBytes
  1761             + y * dstStride + x * dstBpp;
  1762         
  1763         M3Gint numLines = height, numPixels = width;
  1764         M3Gbool paletted = (img->flags & M3G_PALETTED) != 0;
  1765         
  1766         /* Optimize the copy for full image width */
  1767         
  1768         if (width == img->width) {
  1769             numLines = 1;
  1770             numPixels = width * height;
  1771         }
  1772         
  1773         /* Copy a scanline at a time, converting as necessary */
  1774         
  1775         while (numLines-- > 0) {
  1776             
  1777             /* Matching pixel formats are just copied without
  1778              * conversion, and all internally supported paletted
  1779              * formats match each other physically, so they can be
  1780              * copied as well */
  1781         
  1782             if (dstFormat == srcFormat || img->paletteBytes > 0) {
  1783                 m3gCopy(dstPixels, srcPixels, numPixels * dstBpp);
  1784             }
  1785             else {
  1786                 if (!paletted) {
  1787 
  1788                     /* Ordinary conversion into an internal format
  1789                      * that is encoded differently from the external
  1790                      * format; can not be a paletted image */
  1791 
  1792                     M3G_ASSERT((img->flags & M3G_PALETTED) == 0);
  1793                     m3gConvertPixels(srcFormat, srcPixels,
  1794                                      dstFormat, dstPixels,
  1795                                      numPixels);
  1796                 }
  1797                 else {
  1798                     M3G_ASSERT(!m3gSupportedPaletteFormat(img->format));
  1799                     
  1800                     /* Palette indices for one-byte-per-pixel formats
  1801                      * are just copied in and mapped to actual values
  1802                      * later; multibyte paletted formats require a
  1803                      * conversion into LA, RGB, or RGBA format
  1804                      * intensity levels temporarily before remapping
  1805                      * to actual colors in m3gSetImagePalette */
  1806                     
  1807                     if (dstBpp == 1) {
  1808                         m3gCopy(dstPixels, srcPixels, numPixels);
  1809                     }
  1810                     else {
  1811                         m3gConvertPixels(M3G_L8, srcPixels,
  1812                                          dstFormat, dstPixels,
  1813                                          numPixels);
  1814                     }
  1815                 }
  1816             }
  1817             
  1818             srcPixels += srcStride;
  1819             dstPixels += dstStride;
  1820         }
  1821 
  1822         /* Release the image data and invalidate mipmap levels */
  1823         
  1824         m3gUnmapObject(m3g, img->data);
  1825         m3gInvalidateImage(img);
  1826     }
  1827     M3G_VALIDATE_OBJECT(img);
  1828 }
  1829 
  1830 #undef SPAN_BUFFER_SIZE
  1831