m3g/m3gcore11/src/m3g_image.c
changeset 0 5d03bc08d59c
child 11 fed1595b188e
equal deleted inserted replaced
-1:000000000000 0:5d03bc08d59c
       
     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_BGR8_32:
       
   811     case M3G_RGB8_32:
       
   812         return 4;
       
   813     default:
       
   814         M3G_ASSERT(M3G_FALSE);
       
   815         return 0;
       
   816     }
       
   817 }
       
   818 
       
   819 /*!
       
   820  * \internal
       
   821  * \brief Converts pixels between formats
       
   822  *
       
   823  * \note Only a limited subset of source and destination formats may
       
   824  * be supported; see the \c convert functions in m3g_image.c
       
   825  *
       
   826  * \param srcFormat source format
       
   827  * \param src       source pixels
       
   828  * \param dstFormat destination format
       
   829  * \param dst       destination pixels
       
   830  * \param count     pixel count
       
   831  */
       
   832 static void m3gConvertPixels(M3GPixelFormat srcFormat, const M3Gubyte *src,
       
   833                              M3GPixelFormat dstFormat, M3Gubyte *dst,
       
   834                              M3Gsizei count)
       
   835 {
       
   836     M3Guint temp[SPAN_BUFFER_SIZE];
       
   837 
       
   838     M3Guint srcBpp = m3gBytesPerPixel(srcFormat);
       
   839     M3Guint dstBpp = m3gBytesPerPixel(dstFormat);
       
   840     M3G_ASSERT(srcBpp > 0 && dstBpp > 0);
       
   841 
       
   842     while (count > 0) {
       
   843         M3Gsizei n = (count < SPAN_BUFFER_SIZE) ? count : SPAN_BUFFER_SIZE;
       
   844         convertToARGB(srcFormat, src, n, temp);
       
   845         convertFromARGB(temp, n, dstFormat, dst);
       
   846         count -= SPAN_BUFFER_SIZE; /* \note may go negative */
       
   847         src += n * srcBpp;
       
   848         dst += n * dstBpp;
       
   849     }
       
   850 }
       
   851 
       
   852 /*!
       
   853  * \internal
       
   854  * \brief Copies image data. The source image is copied to
       
   855  * the destination image.
       
   856  *
       
   857  * \param dst destination image
       
   858  * \param src source image
       
   859  */
       
   860 static void m3gCopyImagePixels(Image *dst,
       
   861                                const Image *src)
       
   862 {
       
   863     const M3Gubyte *pSrc;
       
   864     M3Gubyte *pDst;
       
   865     M3Gint bpp;
       
   866 
       
   867     /* Check inputs (debug only!) */
       
   868     M3G_VALIDATE_OBJECT(dst);
       
   869     M3G_VALIDATE_OBJECT(src);
       
   870 
       
   871     M3G_ASSERT(src->internalFormat == dst->internalFormat);
       
   872     M3G_ASSERT(src->format == dst->format);
       
   873 
       
   874     M3G_ASSERT(src->paletteBytes == dst->paletteBytes);
       
   875     
       
   876     /* Compute source and destination pixel data pointers */
       
   877     pSrc = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(src), src->data);
       
   878     pDst = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(dst), dst->data);
       
   879 
       
   880     bpp = m3gBytesPerPixel(src->internalFormat);
       
   881 
       
   882     if (src->paletteBytes > 0) {
       
   883         m3gCopy(pDst, pSrc, src->paletteBytes);
       
   884         pDst += dst->paletteBytes;
       
   885         pSrc += src->paletteBytes;
       
   886     }
       
   887 
       
   888     /* Do a straight copy if the sizes match, or resample if not */
       
   889     if (src->width == dst->width && src->height == dst->height ) {
       
   890         m3gCopy(pDst, pSrc, src->width * src->height * bpp);
       
   891     }
       
   892     else {
       
   893         /* Adder values as 8.8 fixed point */
       
   894         M3Gint xAdd, yAdd;
       
   895         M3Gint x, y;
       
   896 
       
   897         xAdd = (256 * src->width) / dst->width;
       
   898         yAdd = (256 * src->height) / dst->height;
       
   899 
       
   900         for (y = 0; y < dst->height; y++) {
       
   901             for (x = 0; x < dst->width; x++) {
       
   902                 m3gCopy(pDst, pSrc + bpp * (((xAdd * x) >> 8) + ((yAdd * y) >> 8) * src->width), bpp);
       
   903                 pDst += bpp;
       
   904             }
       
   905         }
       
   906     }
       
   907 
       
   908     m3gUnmapObject(M3G_INTERFACE(dst), dst->data);
       
   909     m3gUnmapObject(M3G_INTERFACE(src), src->data);
       
   910 
       
   911     m3gInvalidateImage(dst);
       
   912 }
       
   913 
       
   914 /*!
       
   915  * \internal
       
   916  * \brief Invalidates any cached data for this image
       
   917  *
       
   918  * Used when rendering to the image.
       
   919  *
       
   920  * \param img Image object
       
   921  */
       
   922 static void m3gInvalidateImage(Image *img)
       
   923 {
       
   924     M3G_VALIDATE_OBJECT(img);
       
   925     img->dirty = M3G_TRUE;
       
   926     
       
   927 #   if !defined(M3G_NGL_TEXTURE_API)
       
   928     if (img->large) {
       
   929         img->large->dirty = M3G_TRUE;
       
   930     }
       
   931 #   endif /*M3G_NGL_TEXTURE_API*/
       
   932 
       
   933     if (img->powerOfTwo != img) {
       
   934         img->powerOfTwoDirty = M3G_TRUE;
       
   935     }
       
   936 }
       
   937 
       
   938 /*!
       
   939  * \internal
       
   940  * \brief Overloaded Object3D method.
       
   941  *
       
   942  * \param originalObj original Image object
       
   943  * \param cloneObj pointer to cloned Image object
       
   944  * \param pairs array for all object-duplicate pairs
       
   945  * \param numPairs number of pairs
       
   946  */
       
   947 static M3Gbool m3gImageDuplicate(const Object *originalObj,
       
   948                                  Object **cloneObj,
       
   949                                  Object **pairs,
       
   950                                  M3Gint *numPairs)
       
   951 {
       
   952     Image *original = (Image *)originalObj;
       
   953     Image *clone;
       
   954 
       
   955     /* If the original image still has its pixel data, make a full
       
   956      * copy -- this is wasteful for immutable images, but the shame's
       
   957      * on the user in that case */
       
   958     
       
   959     if (original->data) {
       
   960         clone = (Image*) m3gCreateImage(originalObj->interface,
       
   961                                         original->format,
       
   962                                         original->width,
       
   963                                         original->height,
       
   964                                         original->flags);
       
   965     }
       
   966     else {
       
   967 
       
   968         /* Otherwise, just point to the original and use its data
       
   969          * buffers */
       
   970         
       
   971         clone = (Image*) m3gAlloc(M3G_INTERFACE(original), sizeof(*clone));
       
   972         *clone = *original;
       
   973         M3G_ASSIGN_REF(clone->copyOf, original);
       
   974     }
       
   975     
       
   976     *cloneObj = (Object *)clone;
       
   977     if (*cloneObj == NULL) {
       
   978         return M3G_FALSE;
       
   979     }
       
   980 
       
   981     if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
       
   982         /* Copy image contents */
       
   983         if (original->data) {
       
   984             m3gCopyImagePixels(clone, original);
       
   985         }
       
   986         return M3G_TRUE;
       
   987     }
       
   988     else {
       
   989         return M3G_FALSE;
       
   990     }
       
   991 }
       
   992 
       
   993 /*!
       
   994  * \internal
       
   995  *
       
   996  * \brief Frees the pixel data associated with this image; used for
       
   997  * optimizing memory usage after copying the data to a secondary
       
   998  * location
       
   999  */
       
  1000 static void m3gFreeImageData(Image *img)
       
  1001 {
       
  1002     M3G_ASSERT(img->format == M3G_RGB || img->format == M3G_LUMINANCE);
       
  1003 #   if !defined(M3G_NGL_TEXTURE_API)
       
  1004     M3G_ASSERT(!img->mipmapsDirty);
       
  1005 #   endif
       
  1006     M3G_ASSERT(!img->powerOfTwoDirty);
       
  1007     M3G_ASSERT(img->powerOfTwo != NULL);
       
  1008     M3G_ASSERT(!img->pinned);
       
  1009     
       
  1010     M3G_LOG1(M3G_LOG_IMAGES, "Freeing copy of image 0x%08X\n",
       
  1011              (unsigned) img);
       
  1012 
       
  1013     if (!img->copyOf) {
       
  1014         m3gFreeObject(M3G_INTERFACE(img), img->data);
       
  1015         img->data = 0;
       
  1016         m3gFreeObject(M3G_INTERFACE(img), img->mipData);
       
  1017         img->mipData = 0;
       
  1018     }
       
  1019     M3G_ASSIGN_REF(img->copyOf, NULL);
       
  1020 }
       
  1021 
       
  1022 /*!
       
  1023  * \internal
       
  1024  * \brief Returns a power-of-two variant of an image
       
  1025  *
       
  1026  * This is used for sprites and background images.
       
  1027  */
       
  1028 static Image *m3gGetPowerOfTwoImage(Image *img)
       
  1029 {
       
  1030     M3G_VALIDATE_OBJECT(img);
       
  1031     
       
  1032     /* Create a power-of-two variant of the image if one doesn't exist
       
  1033      * already */
       
  1034     
       
  1035     if (img->powerOfTwo == NULL) {
       
  1036 
       
  1037         M3Gint width, height;
       
  1038         M3Gbitmask flags;
       
  1039         Image *potImage;
       
  1040         
       
  1041         M3G_ASSERT(!m3gIsPowerOfTwo(img->width) ||
       
  1042                    !m3gIsPowerOfTwo(img->height));
       
  1043         
       
  1044         /* Choose new image size to allow a maximum shrinkage of 25%;
       
  1045          * this is to weed out pathological cases of quadruple memory
       
  1046          * usage because an image is one pixel too wide */
       
  1047 
       
  1048         width  = m3gNextPowerOfTwo((img->width * 3) >> 2);
       
  1049         height = m3gNextPowerOfTwo((img->height * 3) >> 2);
       
  1050         
       
  1051         width  = M3G_MIN(width, M3G_MAX_TEXTURE_DIMENSION);
       
  1052         height = M3G_MIN(height, M3G_MAX_TEXTURE_DIMENSION);
       
  1053         
       
  1054         flags = img->flags & (~M3G_RENDERING_TARGET);
       
  1055         
       
  1056         potImage = m3gCreateImage(M3G_INTERFACE(img),
       
  1057                                   img->format,
       
  1058                                   width, height,
       
  1059                                   flags);
       
  1060         if (!potImage) {
       
  1061             return NULL; /* automatic out-of-memory */
       
  1062         }
       
  1063 
       
  1064         M3G_ASSIGN_REF(img->powerOfTwo, potImage);
       
  1065         img->powerOfTwoDirty = M3G_TRUE;
       
  1066     }
       
  1067 
       
  1068     /* Update POT image data if necessary */
       
  1069     
       
  1070     if (img->powerOfTwoDirty) {
       
  1071         m3gCopyImagePixels(img->powerOfTwo, img);
       
  1072         img->powerOfTwoDirty = M3G_FALSE;
       
  1073 
       
  1074         /* Get rid of the original at this point if we can */
       
  1075         
       
  1076         if (!img->pinned) {
       
  1077             m3gFreeImageData(img);
       
  1078         }
       
  1079     }
       
  1080 
       
  1081     return img->powerOfTwo;
       
  1082 }
       
  1083 
       
  1084 /*!
       
  1085  * \internal
       
  1086  * \brief Gets image alpha at x, y.
       
  1087  *
       
  1088  * \param image Image object
       
  1089  * \param x x-coordinate
       
  1090  * \param y y-coordinate
       
  1091  * \return alpha value
       
  1092  *
       
  1093  */
       
  1094 static M3Gint m3gGetAlpha(Image *image, M3Gint x, M3Gint y)
       
  1095 {
       
  1096     M3Gint alpha = 255;
       
  1097     M3Gint bpp = m3gBytesPerPixel(image->internalFormat);
       
  1098     M3Guint data = 0;
       
  1099     M3Gubyte *pixels;
       
  1100 
       
  1101     /* Quick exit for non-alpha formats */
       
  1102     
       
  1103     if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
       
  1104         return alpha;
       
  1105     }
       
  1106 
       
  1107     /* For other formats, we have to sample the image data */
       
  1108 
       
  1109     if (!image->data) {
       
  1110         Image *potImage = image->powerOfTwo;
       
  1111         M3G_ASSERT(potImage != image);
       
  1112         return m3gGetAlpha(potImage,
       
  1113                            (x * image->width) / potImage->width,
       
  1114                            (y * image->height) / potImage->height);
       
  1115     }
       
  1116     
       
  1117     pixels = ((M3Gubyte *)m3gMapObject(M3G_INTERFACE(image), image->data));
       
  1118 
       
  1119     if (image->paletteBytes == 0) {
       
  1120         if (bpp == 1) {
       
  1121             data = pixels[x + y * image->width];
       
  1122         }
       
  1123         else if (bpp == 2) {
       
  1124             data = ((M3Gushort *)pixels)[x + y * image->width];
       
  1125         }
       
  1126         else {
       
  1127             data = ((M3Guint *)pixels)[x + y * image->width];
       
  1128         }
       
  1129     }
       
  1130     else {
       
  1131         M3Guint *palette;
       
  1132         palette = (M3Guint *)pixels;
       
  1133         pixels += image->paletteBytes;
       
  1134 
       
  1135         data = palette[pixels[x + y * image->width]];
       
  1136     }
       
  1137 
       
  1138     m3gUnmapObject(M3G_INTERFACE(image), image->data);
       
  1139 
       
  1140     switch (image->internalFormat) {
       
  1141 
       
  1142     case M3G_A8:
       
  1143         alpha = data;
       
  1144         break;
       
  1145     case M3G_LA8:
       
  1146         alpha = data >> 8;
       
  1147         break;
       
  1148     case M3G_RGBA8:
       
  1149         alpha = data >> 24;
       
  1150         break;
       
  1151     default:
       
  1152         /* Should never be here!! */
       
  1153         M3G_ASSERT(M3G_FALSE);
       
  1154     }
       
  1155 
       
  1156     return alpha;
       
  1157 }
       
  1158 
       
  1159 /*!
       
  1160  * \internal
       
  1161  * \brief Computes the scanline stride of an image
       
  1162  */
       
  1163 static M3Gsizei m3gGetImageStride(const Image *img)
       
  1164 {
       
  1165     M3G_VALIDATE_OBJECT(img);
       
  1166     return img->width * m3gBytesPerPixel(img->internalFormat);
       
  1167 }
       
  1168 
       
  1169 /*----------------------------------------------------------------------
       
  1170  * Virtual function table
       
  1171  *--------------------------------------------------------------------*/
       
  1172 
       
  1173 static const ObjectVFTable m3gvf_Image = {
       
  1174     m3gObjectApplyAnimation,
       
  1175     m3gObjectIsCompatible,
       
  1176     m3gObjectUpdateProperty,
       
  1177     m3gObjectDoGetReferences,
       
  1178     m3gObjectFindID,
       
  1179     m3gImageDuplicate,
       
  1180     m3gDestroyImage
       
  1181 };
       
  1182 
       
  1183 
       
  1184 /*----------------------------------------------------------------------
       
  1185  * Public API functions
       
  1186  *--------------------------------------------------------------------*/
       
  1187 
       
  1188 /*!
       
  1189  * \brief Creates a new Image
       
  1190  *
       
  1191  * \param interface     M3G interface
       
  1192  * \param srcFormat     source format
       
  1193  * \param width         width in pixels
       
  1194  * \param height        height in pixels
       
  1195  * \param flags         creation flags; a combination of
       
  1196  *                      M3G_DYNAMIC, M3G_STATIC,
       
  1197  *                      M3G_RENDERING_TARGET, and M3G_PALETTED
       
  1198  * \retval Image new Image object
       
  1199  * \retval NULL Image creating failed
       
  1200  */
       
  1201 M3G_API M3GImage m3gCreateImage(/*@dependent@*/ M3GInterface interface,
       
  1202                                 M3GImageFormat srcFormat,
       
  1203                                 M3Gint width, M3Gint height,
       
  1204                                 M3Gbitmask flags)
       
  1205 {
       
  1206     Interface *m3g = (Interface *) interface;
       
  1207     M3G_VALIDATE_INTERFACE(m3g);
       
  1208     
       
  1209     /* Check errors */
       
  1210     
       
  1211     if (width <= 0 || height <= 0) {
       
  1212         m3gRaiseError(m3g, M3G_INVALID_VALUE);
       
  1213         return NULL;
       
  1214     }
       
  1215 
       
  1216     if (!m3gInRange(srcFormat, M3G_ALPHA, M3G_RGBA)) {
       
  1217         m3gRaiseError(m3g, M3G_INVALID_ENUM);
       
  1218         return NULL;
       
  1219     }
       
  1220 
       
  1221     /* Parameters OK; allocate and initialize the object */
       
  1222 
       
  1223     {
       
  1224         Image *img = m3gAllocZ(m3g, sizeof(Image));
       
  1225         if (img == NULL) {
       
  1226             return NULL;
       
  1227         }
       
  1228 
       
  1229         /* Clean up and set flags */
       
  1230 
       
  1231         M3G_LOG3(M3G_LOG_IMAGES, "Image 0x%08X is %d x %d",
       
  1232                  (unsigned) img, width, height);
       
  1233         
       
  1234         flags |= M3G_DYNAMIC;   /* the default */
       
  1235         
       
  1236         if (flags & M3G_STATIC) {
       
  1237             M3G_LOG(M3G_LOG_IMAGES, ", immutable");
       
  1238             flags &= ~M3G_DYNAMIC;
       
  1239         }
       
  1240         if (flags & M3G_RENDERING_TARGET) {
       
  1241             M3G_LOG(M3G_LOG_IMAGES, ", rendertarget");
       
  1242             flags |= M3G_DYNAMIC;
       
  1243         }
       
  1244         if (flags & M3G_PALETTED) {
       
  1245             M3G_LOG(M3G_LOG_IMAGES, ", paletted");
       
  1246         }
       
  1247         img->flags = flags;
       
  1248 
       
  1249         M3G_LOG(M3G_LOG_IMAGES, "\n");
       
  1250         
       
  1251         {
       
  1252             /* Allocate pixel & palette data; the palette is stored at
       
  1253              * the beginning of the pixel data chunk */
       
  1254 
       
  1255             /* \ comment */
       
  1256             M3Gbool paletted = ((img->flags & M3G_PALETTED) != 0)
       
  1257                 && m3gSupportedPaletteFormat(srcFormat);
       
  1258             M3GPixelFormat internalFormat = getInternalFormat(srcFormat,
       
  1259                                                               paletted);
       
  1260             M3Guint bpp = m3gBytesPerPixel(internalFormat);
       
  1261             M3Guint pixelBytes = width * height * bpp;
       
  1262 
       
  1263             if ((img->flags & M3G_PALETTED) != 0 && !paletted) {
       
  1264                 M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_IMAGES,
       
  1265                         "Warning: Unsupported paletted format\n");
       
  1266             }
       
  1267                 
       
  1268             /* The palette will always have 256 elements and one byte
       
  1269              * per color component (except padded 32-bit for NGL) */
       
  1270             
       
  1271             if (paletted) {
       
  1272                 img->paletteBytes =
       
  1273 #                   if defined(M3G_NGL_TEXTURE_API)
       
  1274                     256 * 4;
       
  1275 #                   else
       
  1276                     256 * m3gBytesPerPixel(m3gPixelFormat(srcFormat));
       
  1277 #                   endif
       
  1278             }
       
  1279 
       
  1280             /* Set up the rest of the image parameters */
       
  1281             
       
  1282             img->width = width;
       
  1283             img->height = height;
       
  1284             img->format = srcFormat;
       
  1285             img->internalFormat = internalFormat;
       
  1286             img->glFormat = m3gGetGLFormat(internalFormat);
       
  1287 
       
  1288             M3G_LOG1(M3G_LOG_IMAGES, "Image data %d bytes\n",
       
  1289                      pixelBytes + img->paletteBytes);
       
  1290             
       
  1291             /* Allocate the image memory */
       
  1292             
       
  1293             img->data = m3gAllocObject(m3g, pixelBytes + img->paletteBytes);
       
  1294             if (img->data == 0) {
       
  1295                 m3gFree(m3g, img);
       
  1296                 return NULL;
       
  1297             }
       
  1298 
       
  1299             /* Lock the image data in memory if the image is dynamic,
       
  1300              * or the format has alpha information; otherwise, we'll
       
  1301              * be able to get rid of an extra copy when generating a
       
  1302              * power-of-two version or uploading to OpenGL */
       
  1303             
       
  1304             if ((img->flags & M3G_DYNAMIC) != 0
       
  1305                 || (img->format != M3G_RGB &&
       
  1306                     img->format != M3G_LUMINANCE)) {
       
  1307                 img->pinned = M3G_TRUE;
       
  1308             }
       
  1309 
       
  1310             /* If the image can be used as a rendering target, clear
       
  1311              * to opaque white by default */
       
  1312             
       
  1313             if ((img->flags & M3G_RENDERING_TARGET) != 0) {
       
  1314                 M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data))
       
  1315                     + img->paletteBytes;
       
  1316                 m3gFill(pixels, (size_t) pixelBytes, -1); 
       
  1317                 m3gUnmapObject(m3g, img->data);
       
  1318             }
       
  1319 
       
  1320             /* Check for "special" images that can't be used as
       
  1321              * textures without some extra trickery */
       
  1322 
       
  1323             if (!m3gIsPowerOfTwo((M3Guint) width) ||
       
  1324                 !m3gIsPowerOfTwo((M3Guint) height)) {
       
  1325                 img->special |= IMG_NPOT;
       
  1326             }
       
  1327             else {
       
  1328                 img->powerOfTwo = img;
       
  1329             }
       
  1330             
       
  1331             if (width > M3G_MAX_TEXTURE_DIMENSION ||
       
  1332                 height > M3G_MAX_TEXTURE_DIMENSION) {
       
  1333                 img->special |= IMG_LARGE;
       
  1334             }
       
  1335         }
       
  1336 
       
  1337         /* Call base class constructor (can not fail) and return */
       
  1338         m3gInitObject(&img->object, m3g, M3G_CLASS_IMAGE);
       
  1339 
       
  1340         M3G_VALIDATE_OBJECT(img);
       
  1341         return (M3GImage) img;
       
  1342     }
       
  1343 }
       
  1344 
       
  1345 /*!
       
  1346  * \brief Prevents further modifications to an image
       
  1347  *
       
  1348  * Essentially, this changes the default M3G_DYNAMIC flag to
       
  1349  * M3G_STATIC; this allows the implementation to make memory and
       
  1350  * performance optimizations not possible for dynamically modified
       
  1351  * images.
       
  1352  */
       
  1353 M3G_API void m3gCommitImage(M3GImage hImage)
       
  1354 {
       
  1355     Image *image = (Image *) hImage;
       
  1356     M3Gbitmask flags;
       
  1357     M3G_VALIDATE_OBJECT(image);
       
  1358     
       
  1359     flags = image->flags;
       
  1360     flags &= ~(M3G_DYNAMIC|M3G_RENDERING_TARGET);
       
  1361     flags |= M3G_STATIC;
       
  1362     
       
  1363     image->flags = flags;
       
  1364 
       
  1365     /* If the image format has no alpha information, we can discard
       
  1366      * the image data under suitable conditions */
       
  1367     
       
  1368     if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
       
  1369         image->pinned = M3G_FALSE;
       
  1370     }
       
  1371     
       
  1372     M3G_LOG1(M3G_LOG_IMAGES, "Image 0x%08X made immutable\n",
       
  1373              (unsigned) image);
       
  1374 }
       
  1375 
       
  1376 /*!
       
  1377  * \brief Check if image is mutable.
       
  1378  * 
       
  1379  * \param hImage Image object
       
  1380  * \retval M3G_TRUE image is mutable
       
  1381  * \retval M3G_FALSE image is immutable
       
  1382  */
       
  1383 M3G_API M3Gbool m3gIsMutable(M3GImage hImage)
       
  1384 {
       
  1385     Image *image = (Image *) hImage;
       
  1386     M3G_VALIDATE_OBJECT(image);
       
  1387     return ((image->flags & M3G_DYNAMIC) != 0);
       
  1388 }
       
  1389 
       
  1390 /*!
       
  1391  * \brief Gets image format as JSR-184 constant
       
  1392  * 
       
  1393  * \param hImage Image object
       
  1394  * \return JSR-184 format
       
  1395  */
       
  1396 M3G_API M3GImageFormat m3gGetFormat(M3GImage hImage)
       
  1397 {
       
  1398     Image *image = (Image *) hImage;
       
  1399     M3G_VALIDATE_OBJECT(image);
       
  1400     return image->format;
       
  1401 }
       
  1402 
       
  1403 /*!
       
  1404  * \brief Gets image width
       
  1405  * 
       
  1406  * \param hImage Image object
       
  1407  * \return width in pixels
       
  1408  */
       
  1409 M3G_API M3Gint m3gGetWidth(M3GImage hImage)
       
  1410 {
       
  1411     Image *image = (Image *) hImage;
       
  1412     M3G_VALIDATE_OBJECT(image);
       
  1413     return image->width;
       
  1414 }
       
  1415 
       
  1416 /*!
       
  1417  * \brief Gets image height
       
  1418  * 
       
  1419  * \param hImage Image object
       
  1420  * \return height in pixels
       
  1421  */
       
  1422 M3G_API M3Gint m3gGetHeight(M3GImage hImage)
       
  1423 {
       
  1424     Image *image = (Image *) hImage;
       
  1425     M3G_VALIDATE_OBJECT(image);
       
  1426     return image->height;
       
  1427 }
       
  1428 
       
  1429 /*!
       
  1430  * \brief Converts a rectangle of pixels of src to dst as srcFormat to
       
  1431  * dstFormat conversion requires.
       
  1432  *
       
  1433  * \param srcFormat source format
       
  1434  * \param src       source pixels
       
  1435  * \param srcStride source stride
       
  1436  * \param width     width in pixels
       
  1437  * \param height    height in pixels
       
  1438  * \param dstFormat destination format
       
  1439  * \param dst       destination pixels
       
  1440  * \param dstStride destination stride
       
  1441  */
       
  1442 static void m3gConvertPixelRect(
       
  1443     M3GPixelFormat srcFormat, const M3Gubyte *src, M3Gsizei srcStride,
       
  1444     M3Gsizei width, M3Gsizei height,
       
  1445     M3GPixelFormat dstFormat, M3Gubyte *dst, M3Gsizei dstStride)
       
  1446 {
       
  1447     /* Detect any fast path cases */
       
  1448     
       
  1449     if ((srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32)
       
  1450         && dstFormat == M3G_RGBA8) {
       
  1451         if (width > 2 && dstStride == width*4) {
       
  1452 
       
  1453             const char endianTest[4] = { 1, 0, 0, 0 };
       
  1454             if ((*(const int *)endianTest) == 1) {
       
  1455                 fastConvertBGRAToRGBA(src, srcStride, width, height, dst);
       
  1456             }
       
  1457             return;
       
  1458         }
       
  1459     }
       
  1460 
       
  1461     /* No luck, do the generic conversion */
       
  1462         
       
  1463     while (height-- > 0) {
       
  1464         m3gConvertPixels(srcFormat, src, dstFormat, dst, width);
       
  1465         src += srcStride;
       
  1466         dst += dstStride;
       
  1467     }
       
  1468 }
       
  1469 
       
  1470 /*!
       
  1471  * \brief Sets the pixel data for an image
       
  1472  * 
       
  1473  * \param hImage Image object
       
  1474  * \param srcPixels source pixels
       
  1475  */
       
  1476 M3G_API void m3gSetImage(M3GImage hImage, const void *srcPixels)
       
  1477 {
       
  1478     Image *img = (Image *) hImage;
       
  1479     M3G_VALIDATE_OBJECT(img);
       
  1480 
       
  1481     {
       
  1482         M3Gsizei bpp = m3gBytesPerPixel(m3gInputDataFormat(img));
       
  1483         m3gSetSubImage(hImage,
       
  1484                        0, 0, img->width, img->height,
       
  1485                        img->width * img->height * bpp, srcPixels);
       
  1486     }
       
  1487 }
       
  1488 
       
  1489 /*!
       
  1490  * \brief Reads pixel data from an image
       
  1491  *
       
  1492  * \param hImage Image object
       
  1493  * \param pixels output buffer for pixels
       
  1494  */
       
  1495 M3G_API void m3gGetImageARGB(M3GImage hImage, M3Guint *pixels)
       
  1496 {
       
  1497     Interface *m3g;
       
  1498     const Image *img = (const Image *) hImage;
       
  1499     M3G_VALIDATE_OBJECT(img);
       
  1500     m3g = M3G_INTERFACE(img);
       
  1501     
       
  1502     if (!pixels) {
       
  1503         m3gRaiseError(m3g, M3G_NULL_POINTER);
       
  1504         return;
       
  1505     }
       
  1506     
       
  1507     if (img->data) {
       
  1508         const M3Gubyte *src = (const M3Gubyte*) m3gMapObject(m3g, img->data);
       
  1509         convertToARGB(img->internalFormat, src,
       
  1510                       img->width * img->height,
       
  1511                       pixels);
       
  1512         m3gUnmapObject(m3g, img->data);
       
  1513     }
       
  1514 }
       
  1515 
       
  1516 /*!
       
  1517  * \brief Sets the palette for an image
       
  1518  * 
       
  1519  * \param hImage Image object
       
  1520  * \param paletteLength length of the palette
       
  1521  * \param srcPalette palette data
       
  1522  */
       
  1523 M3G_API void m3gSetImagePalette(M3GImage hImage,
       
  1524                                 M3Gint paletteLength,
       
  1525                                 const void *srcPalette)
       
  1526 {
       
  1527     Interface *m3g;
       
  1528     Image *img = (Image *) hImage;
       
  1529     M3G_VALIDATE_OBJECT(img);
       
  1530     m3g = M3G_INTERFACE(img);
       
  1531 
       
  1532     /* Check for errors */
       
  1533 
       
  1534     if (img->data == 0 || (img->flags & M3G_STATIC) != 0
       
  1535             || (img->flags & M3G_PALETTED) == 0) {
       
  1536         M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
       
  1537         m3gRaiseError(m3g, M3G_INVALID_OPERATION);
       
  1538         return;
       
  1539     }
       
  1540     if (srcPalette == NULL) {
       
  1541         m3gRaiseError(m3g, M3G_NULL_POINTER);
       
  1542         return;
       
  1543     }
       
  1544     if (!m3gInRange(paletteLength, 0, 256)) {
       
  1545         m3gRaiseError(m3g, M3G_INVALID_VALUE);
       
  1546         return;
       
  1547     }
       
  1548 
       
  1549     /*
       
  1550      * Copy the palette data into the allocated palette (for natively
       
  1551      * supported paletted formats), or remap the existing image data
       
  1552      * using the supplied palette entries (for non-native formats)
       
  1553      *
       
  1554      * NOTE the latter is a one-time operation!
       
  1555      */
       
  1556     if (img->paletteBytes > 0) {
       
  1557         M3Gubyte *palette = (M3Gubyte *)m3gMapObject(m3g, img->data);
       
  1558 #       if defined(M3G_NGL_TEXTURE_API)
       
  1559         m3gConvertPixels(m3gPixelFormat(img->format), srcPalette,
       
  1560                          M3G_RGBA8, palette,
       
  1561                          paletteLength);
       
  1562 #       else
       
  1563         M3Gsizei bpp = m3gBytesPerPixel(m3gPixelFormat(img->format));
       
  1564         m3gCopy(palette, srcPalette, (size_t) paletteLength * bpp);
       
  1565 #       endif
       
  1566     }
       
  1567     else {
       
  1568         M3Gint count = img->width * img->height;
       
  1569         M3Gubyte *pixel = (M3Gubyte*)m3gMapObject(m3g, img->data);
       
  1570         const M3Gubyte *bytePalette = (const M3Gubyte *) srcPalette;
       
  1571 
       
  1572         /* We need to treat the input and internal formats as
       
  1573          * separate, as the internal storage may be padded to more
       
  1574          * bytes than there are color components */
       
  1575 
       
  1576         M3GPixelFormat paletteFormat = m3gPixelFormat(img->format);
       
  1577         const int numComponents = m3gBytesPerPixel(paletteFormat);
       
  1578         M3GPixelFormat imgFormat = img->internalFormat;
       
  1579         const int imgBpp = m3gBytesPerPixel(imgFormat);
       
  1580 
       
  1581         /* In most cases we can just copy the corresponding palette
       
  1582          * entry on top of each pixel based on the pixel intensity (R
       
  1583          * or L component), but special formats require a more
       
  1584          * complicated conversion.  We just use the (slow) general
       
  1585          * conversion routine, as it already incorporates support for
       
  1586          * all formats. */
       
  1587         
       
  1588         if (imgBpp >= numComponents) {
       
  1589             while (count--) {
       
  1590                 int offset = (*pixel) * numComponents;
       
  1591                 int c;
       
  1592                 for (c = 0; c < numComponents; ++c) {
       
  1593                     *pixel++ = bytePalette[offset + c];
       
  1594                 }
       
  1595                 while (c++ < imgBpp) { /* padding for e.g. 24-bit RGB */
       
  1596                     *pixel++ = 0xFF;
       
  1597                 }
       
  1598             }
       
  1599         }
       
  1600         else {
       
  1601             while (count--) {
       
  1602                 int offset = (*pixel) * numComponents;
       
  1603                 m3gConvertPixels(paletteFormat, &bytePalette[offset],
       
  1604                                  imgFormat, pixel,
       
  1605                                  1);
       
  1606                 pixel += imgBpp;
       
  1607             }
       
  1608         }
       
  1609     }
       
  1610     m3gUnmapObject(m3g, img->data);
       
  1611     m3gInvalidateImage(img);
       
  1612 }
       
  1613 
       
  1614 /*!
       
  1615  * \brief Sets a scanline of an image
       
  1616  * 
       
  1617  * \param hImage Image object
       
  1618  * \param line scanline
       
  1619  * \param trueAlpha M3G_TRUE if the source image has an alpha channel,
       
  1620  *                  M3G_FALSE if it should come from the RGB values;
       
  1621  *                  this only matters for alpha-only destination images
       
  1622  * \param pixels souce pixels
       
  1623  */
       
  1624 M3G_API void m3gSetImageScanline(M3GImage hImage,
       
  1625                                  M3Gint line,
       
  1626                                  M3Gbool trueAlpha,
       
  1627                                  const M3Guint *pixels)
       
  1628 {
       
  1629     Image *img = (Image *) hImage;
       
  1630     M3G_VALIDATE_OBJECT(img);
       
  1631 
       
  1632     if (img->data == 0 || (img->flags & M3G_STATIC) != 0
       
  1633             || img->paletteBytes != 0) {
       
  1634         m3gRaiseError(M3G_INTERFACE(img), M3G_INVALID_OPERATION);
       
  1635         return;
       
  1636     }
       
  1637     
       
  1638     {
       
  1639         Interface *m3g = M3G_INTERFACE(img);
       
  1640         M3Gint stride = img->width * m3gBytesPerPixel(img->internalFormat);
       
  1641         M3Gubyte *dst = ((M3Gubyte *) m3gMapObject(m3g, img->data))
       
  1642             + img->paletteBytes;
       
  1643 
       
  1644 #ifdef M3G_NGL_TEXTURE_API
       
  1645         /* For RGB images without alpha channel, source alpha is
       
  1646          * forced to 0xff. */
       
  1647 
       
  1648         if (img->format == M3G_RGB) {
       
  1649             M3Gint i;
       
  1650             M3Guint argb, *dst;
       
  1651 
       
  1652             dst = (M3Guint *) pixels;
       
  1653 
       
  1654             for (i = 0; i < img->width; i++) {
       
  1655                 argb = *dst | 0xff000000;
       
  1656                 *dst++ = argb;
       
  1657             }
       
  1658         }
       
  1659 #endif
       
  1660 
       
  1661         /* Note that an alpha-only destination format is faked for
       
  1662          * luminance if the source contained no true alpha data; alpha
       
  1663          * is then inferred from the RGB values instead */
       
  1664         
       
  1665         convertFromARGB(pixels,
       
  1666                         img->width,
       
  1667                         (img->internalFormat == M3G_A8 && !trueAlpha) ? M3G_L8 : img->internalFormat,
       
  1668                         dst + line * stride);
       
  1669 
       
  1670         m3gUnmapObject(m3g, img->data);
       
  1671         m3gInvalidateImage(img);
       
  1672     }
       
  1673 }
       
  1674 
       
  1675 /*!
       
  1676  * \brief Sets a rectangular subregion of an image
       
  1677  * 
       
  1678  * \param hImage Image object
       
  1679  * \param x x-coordinate in destination image
       
  1680  * \param y y-coordinate in destination image
       
  1681  * \param width width of source pixels
       
  1682  * \param height height of source pixels
       
  1683  * \param length length of source data, in bytes
       
  1684  * \param pixels source pixels
       
  1685  */
       
  1686 M3G_API void m3gSetSubImage(M3GImage hImage,
       
  1687                             M3Gint x, M3Gint y,
       
  1688                             M3Gint width, M3Gint height,
       
  1689                             M3Gint length, const void *pixels)
       
  1690 {
       
  1691     Interface *m3g;
       
  1692     Image *img = (Image *) hImage;
       
  1693 
       
  1694     M3GPixelFormat srcFormat;
       
  1695     M3Gsizei srcBpp;
       
  1696 
       
  1697     M3G_VALIDATE_OBJECT(img);
       
  1698     m3g = M3G_INTERFACE(img);
       
  1699 
       
  1700     /* Check for errors */
       
  1701 
       
  1702     if (img->data == 0 || (img->flags & M3G_STATIC) != 0) {
       
  1703         M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
       
  1704         m3gRaiseError(m3g, M3G_INVALID_OPERATION);
       
  1705         return;
       
  1706     }
       
  1707     if (pixels == NULL) {
       
  1708         m3gRaiseError(m3g, M3G_INVALID_VALUE);
       
  1709         return;
       
  1710     }
       
  1711     if (x < 0 || y < 0 || width <= 0 || height <= 0
       
  1712             || x+width > img->width || y+height > img->height) {
       
  1713         m3gRaiseError(m3g, M3G_INVALID_VALUE);
       
  1714         return;
       
  1715     }
       
  1716     
       
  1717     srcFormat = m3gInputDataFormat(img);
       
  1718     srcBpp = m3gBytesPerPixel(srcFormat);
       
  1719     
       
  1720     if (length < width * height * srcBpp) {
       
  1721         m3gRaiseError(m3g, M3G_INVALID_VALUE);
       
  1722         return;
       
  1723     }    
       
  1724 
       
  1725     /* Copy the image data, doing a conversion if the input format
       
  1726      * does not match the internal storage format */
       
  1727     {
       
  1728         const M3Gubyte *srcPixels = (const M3Gubyte*) pixels;
       
  1729         M3Gsizei srcStride = width * srcBpp;
       
  1730 
       
  1731         M3GPixelFormat dstFormat = img->internalFormat;
       
  1732         M3Gsizei dstBpp = m3gBytesPerPixel(dstFormat);
       
  1733         M3Gsizei dstStride = img->width * dstBpp;
       
  1734         M3Gubyte *dstPixels =
       
  1735             ((M3Gubyte *)m3gMapObject(m3g, img->data))
       
  1736             + img->paletteBytes
       
  1737             + y * dstStride + x * dstBpp;
       
  1738         
       
  1739         M3Gint numLines = height, numPixels = width;
       
  1740         M3Gbool paletted = (img->flags & M3G_PALETTED) != 0;
       
  1741         
       
  1742         /* Optimize the copy for full image width */
       
  1743         
       
  1744         if (width == img->width) {
       
  1745             numLines = 1;
       
  1746             numPixels = width * height;
       
  1747         }
       
  1748         
       
  1749         /* Copy a scanline at a time, converting as necessary */
       
  1750         
       
  1751         while (numLines-- > 0) {
       
  1752             
       
  1753             /* Matching pixel formats are just copied without
       
  1754              * conversion, and all internally supported paletted
       
  1755              * formats match each other physically, so they can be
       
  1756              * copied as well */
       
  1757         
       
  1758             if (dstFormat == srcFormat || img->paletteBytes > 0) {
       
  1759                 m3gCopy(dstPixels, srcPixels, numPixels * dstBpp);
       
  1760             }
       
  1761             else {
       
  1762                 if (!paletted) {
       
  1763 
       
  1764                     /* Ordinary conversion into an internal format
       
  1765                      * that is encoded differently from the external
       
  1766                      * format; can not be a paletted image */
       
  1767 
       
  1768                     M3G_ASSERT((img->flags & M3G_PALETTED) == 0);
       
  1769                     m3gConvertPixels(srcFormat, srcPixels,
       
  1770                                      dstFormat, dstPixels,
       
  1771                                      numPixels);
       
  1772                 }
       
  1773                 else {
       
  1774                     M3G_ASSERT(!m3gSupportedPaletteFormat(img->format));
       
  1775                     
       
  1776                     /* Palette indices for one-byte-per-pixel formats
       
  1777                      * are just copied in and mapped to actual values
       
  1778                      * later; multibyte paletted formats require a
       
  1779                      * conversion into LA, RGB, or RGBA format
       
  1780                      * intensity levels temporarily before remapping
       
  1781                      * to actual colors in m3gSetImagePalette */
       
  1782                     
       
  1783                     if (dstBpp == 1) {
       
  1784                         m3gCopy(dstPixels, srcPixels, numPixels);
       
  1785                     }
       
  1786                     else {
       
  1787                         m3gConvertPixels(M3G_L8, srcPixels,
       
  1788                                          dstFormat, dstPixels,
       
  1789                                          numPixels);
       
  1790                     }
       
  1791                 }
       
  1792             }
       
  1793             
       
  1794             srcPixels += srcStride;
       
  1795             dstPixels += dstStride;
       
  1796         }
       
  1797 
       
  1798         /* Release the image data and invalidate mipmap levels */
       
  1799         
       
  1800         m3gUnmapObject(m3g, img->data);
       
  1801         m3gInvalidateImage(img);
       
  1802     }
       
  1803     M3G_VALIDATE_OBJECT(img);
       
  1804 }
       
  1805 
       
  1806 #undef SPAN_BUFFER_SIZE
       
  1807