os/graphics/m3g/m3gcore11/src/m3g_image.c
changeset 0 bde4ae8d615e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/os/graphics/m3g/m3gcore11/src/m3g_image.c	Fri Jun 15 03:10:57 2012 +0200
     1.3 @@ -0,0 +1,1831 @@
     1.4 +/*
     1.5 +* Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
     1.6 +* All rights reserved.
     1.7 +* This component and the accompanying materials are made available
     1.8 +* under the terms of the License "Eclipse Public License v1.0"
     1.9 +* which accompanies this distribution, and is available
    1.10 +* at the URL "http://www.eclipse.org/legal/epl-v10.html".
    1.11 +*
    1.12 +* Initial Contributors:
    1.13 +* Nokia Corporation - initial contribution.
    1.14 +*
    1.15 +* Contributors:
    1.16 +*
    1.17 +* Description: Image implementation
    1.18 +*
    1.19 +*/
    1.20 +
    1.21 +
    1.22 +/*!
    1.23 + * \internal
    1.24 + * \file
    1.25 + * \brief Image implementation
    1.26 + *
    1.27 + */
    1.28 +
    1.29 +#ifndef M3G_CORE_INCLUDE
    1.30 +#   error included by m3g_core.c; do not compile separately.
    1.31 +#endif
    1.32 +
    1.33 +#include "m3g_image.h"
    1.34 +#include "m3g_texture.h"
    1.35 +
    1.36 +/* Declare prototypes for some of the helper functions called from the
    1.37 + * platform-dependent code included below */
    1.38 +
    1.39 +static M3Gint  m3gBytesPerPixel(M3GPixelFormat format);
    1.40 +static M3Gint  m3gGetNumMipmapLevels(M3Gint w, M3Gint h);
    1.41 +static void m3gDownsample(M3GPixelFormat format, const M3Gubyte *srcPixels, M3Gint *pw, M3Gint *ph, M3Gubyte *dstPixels);
    1.42 +static void m3gFreeImageData(Image *img);
    1.43 +
    1.44 +/* Include platform-dependent functionality */
    1.45 +#include "m3g_image.inl"
    1.46 +
    1.47 +/* Size of the buffer used in pixel format conversions; this should be
    1.48 + * an even number */
    1.49 +#define SPAN_BUFFER_SIZE 32
    1.50 +
    1.51 +M3G_CT_ASSERT((SPAN_BUFFER_SIZE & 1) == 0);
    1.52 +
    1.53 +/*----------------------------------------------------------------------
    1.54 + * Private functions
    1.55 + *--------------------------------------------------------------------*/
    1.56 +
    1.57 +/*!
    1.58 + * \internal
    1.59 + * \brief Destroys this Image object.
    1.60 + *
    1.61 + * \param obj Image object
    1.62 + */
    1.63 +static void m3gDestroyImage(Object *obj)
    1.64 +{
    1.65 +    Image *image = (Image*)obj;
    1.66 +    Interface *m3g = M3G_INTERFACE(image);
    1.67 +    M3G_VALIDATE_OBJECT(image);
    1.68 +
    1.69 +    if (!image->copyOf) {
    1.70 +        m3gFreeObject(m3g, image->data);
    1.71 +        m3gFreeObject(m3g, image->mipData);
    1.72 +    }
    1.73 +    M3G_ASSIGN_REF(image->copyOf, NULL);
    1.74 +    
    1.75 +    if (image->powerOfTwo != image) {
    1.76 +        M3G_ASSIGN_REF(image->powerOfTwo, NULL);
    1.77 +    }
    1.78 +
    1.79 +#   if !defined(M3G_NGL_TEXTURE_API)
    1.80 +    if (image->texObject) {
    1.81 +        m3gDeleteGLTextures(m3g, 1, &image->texObject);
    1.82 +    }
    1.83 +    if (image->large != NULL) {
    1.84 +        m3gDestroyLargeImage(image);
    1.85 +    }
    1.86 +#   endif
    1.87 +
    1.88 +    m3gDestroyObject(obj);
    1.89 +}
    1.90 +
    1.91 +/*--------------------------------------------------------------------*/
    1.92 +
    1.93 +#define RED(argb)   (((argb) >> 16) & 0xFFu)
    1.94 +#define GREEN(argb) (((argb) >>  8) & 0xFFu)
    1.95 +#define BLUE(argb)  ( (argb)        & 0xFFu)
    1.96 +#define ALPHA(argb) (((argb) >> 24) & 0xFFu)
    1.97 +
    1.98 +#define RGBSUM(argb) (0x4CB2u * RED(argb) +     \
    1.99 +                      0x9691u * GREEN(argb) +   \
   1.100 +                      0x1D3Eu * BLUE(argb))
   1.101 +
   1.102 +/*!
   1.103 + * \internal \brief ARGB -> A
   1.104 + */
   1.105 +static void convertARGBToA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.106 +{
   1.107 +    while (count--) {
   1.108 +        *dst++ = (M3Gubyte) ALPHA(*src++);
   1.109 +    }
   1.110 +}
   1.111 +
   1.112 +/*!
   1.113 + * \internal \brief ARGB -> L
   1.114 + */
   1.115 +static void convertARGBToL8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.116 +{
   1.117 +    while (count--) {
   1.118 +        M3Guint argb = *src++;
   1.119 +        M3Guint sum = RGBSUM(argb);
   1.120 +        *dst++ = (M3Gubyte)(sum >> 16);
   1.121 +    }
   1.122 +}
   1.123 +
   1.124 +/*!
   1.125 + * \internal \brief ARGB -> LA */
   1.126 +static void convertARGBToLA4(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.127 +{
   1.128 +    while (count--) {
   1.129 +        M3Guint argb = *src++;
   1.130 +        M3Guint sum = RGBSUM(argb);
   1.131 +        *dst++ = (M3Gubyte)(((sum >> 16) & 0xF0) | ((argb >> 28) & 0x0F));
   1.132 +    }
   1.133 +}
   1.134 +
   1.135 +/*!
   1.136 + * \internal \brief ARGB -> LA8
   1.137 + */
   1.138 +static void convertARGBToLA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.139 +{
   1.140 +    while (count--) {
   1.141 +        M3Guint argb = *src++;
   1.142 +        M3Guint sum = RGBSUM(argb);
   1.143 +        *dst++ = (M3Gubyte)(sum >> 16);       /* L */
   1.144 +        *dst++ = (M3Gubyte) ALPHA(argb);
   1.145 +    }
   1.146 +}
   1.147 +
   1.148 +/*!
   1.149 + * \internal \brief ARGB -> RGB
   1.150 + */
   1.151 +static void convertARGBToRGB8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.152 +{
   1.153 +    while (count--) {
   1.154 +        M3Guint argb = *src++;
   1.155 +        *dst++ = (M3Gubyte) RED(argb);
   1.156 +        *dst++ = (M3Gubyte) GREEN(argb);
   1.157 +        *dst++ = (M3Gubyte) BLUE(argb);
   1.158 +    }
   1.159 +}
   1.160 +
   1.161 +/*!
   1.162 + * \internal \brief ARGB -> RGB565
   1.163 + */
   1.164 +static void convertARGBToRGB565(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.165 +{
   1.166 +    while (count--) {
   1.167 +        M3Guint argb = *src++;
   1.168 +        *(M3Gushort*)dst = (M3Gushort)(((argb >> 8) & 0xF800u)|
   1.169 +                                       ((argb >> 5) & 0x07E0u)|
   1.170 +                                       ((argb >> 3) & 0x001Fu));
   1.171 +        dst += 2;
   1.172 +    }
   1.173 +}
   1.174 +
   1.175 +/*!
   1.176 + * \internal \brief ARGB -> RGBA
   1.177 + */
   1.178 +static void convertARGBToRGBA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.179 +{
   1.180 +    while (count--) {
   1.181 +        M3Guint argb = *src++;
   1.182 +        *dst++ = (M3Gubyte) RED(argb);
   1.183 +        *dst++ = (M3Gubyte) GREEN(argb);
   1.184 +        *dst++ = (M3Gubyte) BLUE(argb);
   1.185 +        *dst++ = (M3Gubyte) ALPHA(argb);
   1.186 +    }
   1.187 +}
   1.188 +
   1.189 +/*!
   1.190 + * \internal \brief ARGB -> BGRA
   1.191 + */
   1.192 +static void convertARGBToBGRA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
   1.193 +{
   1.194 +    while (count--) {
   1.195 +        M3Guint argb = *src++;
   1.196 +        *dst++ = (M3Gubyte) BLUE(argb);
   1.197 +        *dst++ = (M3Gubyte) GREEN(argb);
   1.198 +        *dst++ = (M3Gubyte) RED(argb);
   1.199 +        *dst++ = (M3Gubyte) ALPHA(argb);
   1.200 +    }
   1.201 +}
   1.202 +
   1.203 +#undef RED
   1.204 +#undef GREEN
   1.205 +#undef BLUE
   1.206 +#undef ALPHA
   1.207 +
   1.208 +/*!
   1.209 + * \internal
   1.210 + * \brief Converts a span of ARGB pixels to another format
   1.211 + *
   1.212 + * \param src   source pixels
   1.213 + * \param count pixel count
   1.214 + * \param dstFormat destination format
   1.215 + * \param dst destination pixels
   1.216 + */
   1.217 +static void convertFromARGB(const M3Guint *src,
   1.218 +                            M3Gsizei count,
   1.219 +                            M3GPixelFormat dstFormat,
   1.220 +                            M3Gubyte *dst)
   1.221 +{
   1.222 +    switch (dstFormat) {
   1.223 +    case M3G_L8:
   1.224 +        convertARGBToL8(src, count, dst);
   1.225 +        break;
   1.226 +    case M3G_A8:
   1.227 +        convertARGBToA8(src, count, dst);
   1.228 +        break;
   1.229 +    case M3G_LA4:
   1.230 +        convertARGBToLA4(src, count, dst);
   1.231 +        break;
   1.232 +    case M3G_LA8:
   1.233 +        convertARGBToLA8(src, count, dst);
   1.234 +        break;
   1.235 +    case M3G_RGB8:
   1.236 +        convertARGBToRGB8(src, count, dst);
   1.237 +        break;
   1.238 +    case M3G_RGB565:
   1.239 +        convertARGBToRGB565(src, count, dst);
   1.240 +        break;
   1.241 +    case M3G_RGBA8:
   1.242 +    case M3G_RGB8_32:
   1.243 +        convertARGBToRGBA8(src, count, dst);
   1.244 +        break;
   1.245 +    case M3G_BGRA8:
   1.246 +    case M3G_BGR8_32:
   1.247 +        convertARGBToBGRA8(src, count, dst);
   1.248 +        break;
   1.249 +    default:
   1.250 +        M3G_ASSERT(M3G_FALSE);  /* conversion not supported */
   1.251 +    }
   1.252 +}
   1.253 +
   1.254 +/*--------------------------------------------------------------------*/
   1.255 +
   1.256 +/*!
   1.257 + * \internal \brief A8 -> ARGB
   1.258 + */
   1.259 +static void convertA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.260 +{
   1.261 +    while (count--) {
   1.262 +        M3Guint argb = M3G_RGB_MASK;
   1.263 +        argb |= ((M3Guint) *src++) << 24;
   1.264 +        *dst++ = argb;
   1.265 +    }
   1.266 +}
   1.267 +
   1.268 +/*!
   1.269 + * \internal \brief L8 -> ARGB
   1.270 + */
   1.271 +static void convertL8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.272 +{
   1.273 +    while (count--) {
   1.274 +        M3Guint argb = *src++;
   1.275 +        argb |= (argb << 8) | (argb << 16);
   1.276 +        argb |= M3G_ALPHA_MASK;
   1.277 +        *dst++ = argb;
   1.278 +    }
   1.279 +}
   1.280 +
   1.281 +/*!
   1.282 + * \internal \brief LA8 -> ARGB
   1.283 + */
   1.284 +static void convertLA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.285 +{
   1.286 +    while (count--) {
   1.287 +        M3Guint argb = *src++;
   1.288 +        argb |= (argb << 8) | (argb << 16);
   1.289 +        argb |= ((M3Guint) *src++) << 24;
   1.290 +        *dst++ = argb;
   1.291 +    }
   1.292 +}
   1.293 +
   1.294 +/*!
   1.295 + * \internal \brief RGB8 -> ARGB
   1.296 + */
   1.297 +static void convertRGB8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.298 +{
   1.299 +    while (count--) {
   1.300 +        M3Guint argb = M3G_ALPHA_MASK;
   1.301 +        argb |= ((M3Guint)(*src++)) << 16;
   1.302 +        argb |= ((M3Guint)(*src++)) <<  8;
   1.303 +        argb |=  (M3Guint)(*src++);
   1.304 +        *dst++ = argb;
   1.305 +    }
   1.306 +}
   1.307 +
   1.308 +/*!
   1.309 + * \internal \brief RGB565 -> ARGB
   1.310 + */
   1.311 +static void convertRGB565ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.312 +{
   1.313 +    while (count--) {
   1.314 +        M3Guint argb = M3G_ALPHA_MASK;
   1.315 +        const M3Guint rgb565 = *(const M3Gushort*)src;
   1.316 +        argb |= ((rgb565 & 0xF800u) << 8)|((rgb565 & 0xE000u) << 3);
   1.317 +        argb |= ((rgb565 & 0x07E0u) << 5)|((rgb565 & 0x0600u) >> 1);
   1.318 +        argb |= ((rgb565 & 0x001Fu) << 3)|((rgb565 & 0x001Cu) >> 2);
   1.319 +        *dst++ = argb;
   1.320 +        src += 2;
   1.321 +    }
   1.322 +}
   1.323 +
   1.324 +/*!
   1.325 + * \internal \brief RGBA8 -> ARGB
   1.326 + */
   1.327 +static void convertRGBA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.328 +{
   1.329 +    while (count--) {
   1.330 +        M3Guint argb;
   1.331 +        argb  = ((M3Guint)(*src++)) << 16;
   1.332 +        argb |= ((M3Guint)(*src++)) <<  8;
   1.333 +        argb |=  (M3Guint)(*src++);
   1.334 +        argb |= ((M3Guint)(*src++)) << 24;
   1.335 +        *dst++ = argb;
   1.336 +    }
   1.337 +}
   1.338 +
   1.339 +/*!
   1.340 + * \internal \brief BGRA8 -> ARGB
   1.341 + */
   1.342 +static void convertBGRA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
   1.343 +{
   1.344 +    while (count--) {
   1.345 +        M3Guint argb;
   1.346 +        argb  =  (M3Guint)(*src++);
   1.347 +        argb |= ((M3Guint)(*src++)) <<  8;
   1.348 +        argb |= ((M3Guint)(*src++)) << 16;
   1.349 +        argb |= ((M3Guint)(*src++)) << 24;
   1.350 +        *dst++ = argb;
   1.351 +    }
   1.352 +}
   1.353 +
   1.354 +/*!
   1.355 + * \internal
   1.356 + * \brief Converts a span of pixels to ARGB
   1.357 + *
   1.358 + * \param srcFormat source format
   1.359 + * \param src   source pixels
   1.360 + * \param count pixel count
   1.361 + * \param dst destination pixels
   1.362 + */
   1.363 +static void convertToARGB(M3GPixelFormat srcFormat,
   1.364 +                          const M3Gubyte *src,
   1.365 +                          M3Gsizei count,
   1.366 +                          M3Guint *dst)
   1.367 +{
   1.368 +    switch (srcFormat) {
   1.369 +    case M3G_A8:
   1.370 +        convertA8ToARGB(src, count, dst);
   1.371 +        break;
   1.372 +    case M3G_L8:
   1.373 +        convertL8ToARGB(src, count, dst);
   1.374 +        break;
   1.375 +    case M3G_LA8:
   1.376 +        convertLA8ToARGB(src, count, dst);
   1.377 +        break;
   1.378 +    case M3G_RGB8:
   1.379 +        convertRGB8ToARGB(src, count, dst);
   1.380 +        break;
   1.381 +    case M3G_RGB565:
   1.382 +        convertRGB565ToARGB(src, count, dst);
   1.383 +        break;
   1.384 +    case M3G_RGBA8:
   1.385 +    case M3G_RGB8_32:
   1.386 +        convertRGBA8ToARGB(src, count, dst);
   1.387 +        break;
   1.388 +    case M3G_BGRA8:
   1.389 +    case M3G_BGR8_32:
   1.390 +        convertBGRA8ToARGB(src, count, dst);
   1.391 +        break;
   1.392 +    default:
   1.393 +        M3G_ASSERT(M3G_FALSE);  /* conversion not supported */
   1.394 +    }
   1.395 +}
   1.396 +
   1.397 +/*!
   1.398 + * \internal
   1.399 + * \brief Fast path for BGRA-to-RGBA conversion
   1.400 + */
   1.401 +#if defined (M3G_HW_ARMV6)
   1.402 +__asm void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
   1.403 +                                 M3Gsizei width, M3Gsizei height,
   1.404 +                                 M3Gubyte *dst)
   1.405 +{
   1.406 +// r0 = *src
   1.407 +// r1 = srcStride
   1.408 +// r2 = width
   1.409 +// r3 = height
   1.410 +// sp[0] = *dst
   1.411 +
   1.412 +		CODE32
   1.413 +
   1.414 +		CMP		r3, #0				// if height = 0, do nothing
   1.415 +		BXEQ	lr
   1.416 +
   1.417 +  		STMFD	sp!, {r4-r12, lr} 
   1.418 +
   1.419 +		LDR		r12, [sp, #(10*4)]
   1.420 +		SUB		r1, r1, r2, LSL #2
   1.421 +		MOV		r14, r2
   1.422 +
   1.423 +_fastConvertBGRAToRGBA_outerLoop
   1.424 +		MOVS	r2, r14, ASR #2			// amount of 4x32 bit writes
   1.425 +		BEQ		_fastConvertBGRAToRGBA_tail
   1.426 +
   1.427 +_fastConvertBGRAToRGBA_innerLoop
   1.428 +
   1.429 +		LDMIA	r0!, {r4-r7}		// AARRGGBB
   1.430 +		SUBS	r2, #1
   1.431 +		REV		r4, r4				// BBGGRRAA
   1.432 +		REV		r5, r5
   1.433 +		REV		r6, r6				
   1.434 +		REV		r7, r7
   1.435 +		MOV		r8, r4, ROR #8		// AABBGGRR
   1.436 +		MOV		r9, r5, ROR #8
   1.437 +		MOV		r10, r6, ROR #8		
   1.438 +		MOV		r11, r7, ROR #8
   1.439 +		STMIA	r12!, {r8-r11}
   1.440 +		BNE		_fastConvertBGRAToRGBA_innerLoop
   1.441 +
   1.442 +_fastConvertBGRAToRGBA_tail
   1.443 +		MOV 	r2, r14, ASR #2
   1.444 +		SUBS	r2, r14, r2, LSL #2		// number of remaining writes in the tail
   1.445 +
   1.446 +_fastConvertBGRAToRGBA_tail_loop
   1.447 +
   1.448 +		LDRNE	r4, [r0], #4
   1.449 +		REVNE	r4, r4
   1.450 +		MOVNE	r8, r4, ROR #8
   1.451 +		STRNE	r8, [r12], #4
   1.452 +		SUBNES	r2, #1
   1.453 +		BNE		_fastConvertBGRAToRGBA_tail_loop
   1.454 +
   1.455 +		SUBS	r3, #1
   1.456 +		ADD		r0, r0, r1
   1.457 +		BNE		_fastConvertBGRAToRGBA_outerLoop
   1.458 +
   1.459 +		LDMFD	sp!, {r4-r12, lr}
   1.460 +		BX		lr
   1.461 +
   1.462 +}
   1.463 +#else /* #if defined (M3G_HW_ARMV6) */
   1.464 +static void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
   1.465 +                                  M3Gsizei width, M3Gsizei height,
   1.466 +                                  M3Gubyte *dst)
   1.467 +{
   1.468 +    unsigned int pixel, pixel2;
   1.469 +    unsigned int temp;
   1.470 +    unsigned int mask = 0x00ff00ff;
   1.471 +    int spanwidth = (width >> 1) - 1;
   1.472 +    int x, y;
   1.473 +    unsigned int *d = (unsigned int *)dst;
   1.474 +
   1.475 +    M3G_ASSERT(width > 2);
   1.476 +    
   1.477 +    for (y = 0; y < height; ++y) {
   1.478 +        unsigned int *s = (unsigned int *)(src + y*srcStride);
   1.479 +
   1.480 +        pixel = *s++;
   1.481 +
   1.482 +        for (x = 0; x < spanwidth; ++x) {
   1.483 +            pixel2 = *s++;
   1.484 +
   1.485 +            temp   = pixel & mask;          /* 00RR00BB */
   1.486 +            pixel  = pixel - temp;          /* AA00GG00 */
   1.487 +            pixel  = pixel | (temp << 16);  /* AABBGG00 */
   1.488 +            *d++   = pixel | (temp >> 16);  /* AABBGGRR */
   1.489 +
   1.490 +            pixel = *s++;
   1.491 +
   1.492 +            temp   = pixel2 & mask;          /* 00RR00BB */
   1.493 +            pixel2 = pixel2 - temp;          /* AA00GG00 */
   1.494 +            pixel2 = pixel2 | (temp << 16);  /* AABBGG00 */
   1.495 +            *d++   = pixel2 | (temp >> 16);  /* AABBGGRR */
   1.496 +        }
   1.497 +        
   1.498 +        pixel2 = *s++;
   1.499 +        temp   = pixel & mask;          /* 00RR00BB */
   1.500 +        pixel  = pixel - temp;          /* AA00GG00 */
   1.501 +        pixel  = pixel | (temp << 16);  /* AABBGG00 */
   1.502 +        *d++   = pixel | (temp >> 16);  /* AABBGGRR */
   1.503 +
   1.504 +        temp   = pixel2 & mask;          /* 00RR00BB */
   1.505 +        pixel2 = pixel2 - temp;          /* AA00GG00 */
   1.506 +        pixel2 = pixel2 | (temp << 16);  /* AABBGG00 */
   1.507 +        *d++   = pixel2 | (temp >> 16);  /* AABBGGRR */
   1.508 +    }
   1.509 +}
   1.510 +#endif /* #if defined (M3G_HW_ARMV6) */
   1.511 +
   1.512 +/*--------------------------------------------------------------------*/
   1.513 +
   1.514 +/*!
   1.515 + * \internal
   1.516 + * \brief Maps a logical image format to an internal pixel format
   1.517 + *
   1.518 + * \param imgFormat logical image format
   1.519 + * \param paletted  paletted flag
   1.520 + * \return the internal image pixel format
   1.521 + */
   1.522 +static M3GPixelFormat getInternalFormat(M3GImageFormat imgFormat,
   1.523 +                                        M3Gbool paletted)
   1.524 +{
   1.525 +    if (paletted) {
   1.526 +        switch (imgFormat) {
   1.527 +        case M3G_RGB:
   1.528 +#           if defined(M3G_NGL_TEXTURE_API)
   1.529 +            return M3G_PALETTE8_RGB8_32;
   1.530 +#           else
   1.531 +            return M3G_PALETTE8_RGB8;
   1.532 +#           endif
   1.533 +        case M3G_RGBA:
   1.534 +            return M3G_PALETTE8_RGBA8;
   1.535 +        default:
   1.536 +            M3G_ASSERT(M3G_FALSE);
   1.537 +            return (M3GPixelFormat)0;
   1.538 +        }
   1.539 +    }
   1.540 +    else {
   1.541 +        M3GPixelFormat format = m3gPixelFormat(imgFormat);
   1.542 +        
   1.543 +#       if defined(M3G_NGL_TEXTURE_API)
   1.544 +        if (format == M3G_RGB8) {
   1.545 +            return (M3G_USE_16BIT_TEXTURES) ? M3G_RGB565 : M3G_RGB8_32;
   1.546 +        }
   1.547 +        if (format == M3G_LA8) {
   1.548 +            return M3G_LA4;
   1.549 +        }
   1.550 +#       endif
   1.551 +        
   1.552 +        return format;
   1.553 +    }
   1.554 +}
   1.555 +
   1.556 +/*!
   1.557 + * \internal
   1.558 + * \brief Gets the correct pixel format for setting data to an image
   1.559 + */
   1.560 +static M3GPixelFormat m3gInputDataFormat(const Image *img)
   1.561 +{
   1.562 +    /* Any of the paletted formats will do for a paletted image, as
   1.563 +     * they all have 8-bit indices; we pick PALETTE8_RGBA8 here */
   1.564 +    
   1.565 +    if (img->flags & M3G_PALETTED) {
   1.566 +        return M3G_PALETTE8_RGBA8;
   1.567 +    }
   1.568 +    
   1.569 +    return m3gPixelFormat(img->format);
   1.570 +}
   1.571 +
   1.572 +
   1.573 +/*!
   1.574 + * \internal
   1.575 + * \brief Returns log2(resolution)+1. Assumes that resolution is power of two.
   1.576 + *
   1.577 + * \param w width in pixels
   1.578 + * \param h height in pixels
   1.579 + * \return number of needed mipmap levels
   1.580 + */
   1.581 +static M3Gint m3gGetNumMipmapLevels(M3Gint w, M3Gint h)
   1.582 +{
   1.583 +    M3Gint res = (w > h) ? w : h;
   1.584 +    M3Gint levels = 0;
   1.585 +    while (res > 0) {
   1.586 +        ++levels;
   1.587 +        res >>= 1;
   1.588 +    };
   1.589 +    return levels;
   1.590 +}
   1.591 +
   1.592 +/*!
   1.593 + * \internal
   1.594 + * \brief Downsamples an image to half the original size
   1.595 + *
   1.596 + *
   1.597 + * \param format    pixel format
   1.598 + * \param srcPixels source pixels
   1.599 + * \param pw        pointer to width
   1.600 + * \param ph        pointer to height
   1.601 + * \param dstPixels destination pixels
   1.602 + */
   1.603 +static void m3gDownsample(M3GPixelFormat format,
   1.604 +                          const M3Gubyte *srcPixels,
   1.605 +                          M3Gint *pw, M3Gint *ph,
   1.606 +                          M3Gubyte *dstPixels)
   1.607 +{
   1.608 +    M3Gint i, j, bpp, pixStride, lineStride;
   1.609 +    M3Gint w = *pw, h = *ph;
   1.610 +    M3Gubyte *dst;
   1.611 +    M3Guint temp[2][SPAN_BUFFER_SIZE/2];
   1.612 +
   1.613 +    M3G_ASSERT_PTR(srcPixels);
   1.614 +    M3G_ASSERT(w >= 1 && h >= 1);
   1.615 +
   1.616 +    bpp = m3gBytesPerPixel(format);
   1.617 +    lineStride = (h > 1) ? w * bpp : 0;
   1.618 +    pixStride = (w > 1) ? bpp : 0;
   1.619 +
   1.620 +    dst = dstPixels;
   1.621 +
   1.622 +    /* Iterate over buffer-sized blocks in the image */
   1.623 +    
   1.624 +    for (j = 0; j < h; j += 2) {
   1.625 +        for (i = 0; i < w; i += SPAN_BUFFER_SIZE/2) {
   1.626 +            
   1.627 +            /* Fill the buffer from the source image */
   1.628 +            
   1.629 +            const M3Gubyte *src = srcPixels + (j*lineStride + i*pixStride);
   1.630 +            M3Gint c = SPAN_BUFFER_SIZE/2;
   1.631 +            if (w - i < c) {
   1.632 +                c = w - i;
   1.633 +            }
   1.634 +            convertToARGB(format, src, c, &temp[0][0]);
   1.635 +            convertToARGB(format, src + lineStride, c, &temp[1][0]);
   1.636 +            if (w == 1) {
   1.637 +                temp[0][1] = temp[0][0];
   1.638 +                temp[1][1] = temp[1][0];
   1.639 +            }
   1.640 +            
   1.641 +            /* Average the pixels in the buffer */
   1.642 +            {
   1.643 +#               define AG_MASK 0xFF00FF00u
   1.644 +#               define RB_MASK 0x00FF00FFu
   1.645 +                
   1.646 +                M3Gint k;
   1.647 +                for (k = 0; k < c; k += 2) {
   1.648 +                    M3Guint ag, rb;
   1.649 +
   1.650 +                    /* Add two components in parallel */
   1.651 +                    
   1.652 +                    ag =  ((temp[0][k] & AG_MASK) >> 8)
   1.653 +                        + ((temp[1][k] & AG_MASK) >> 8)
   1.654 +                        + ((temp[0][k+1] & AG_MASK) >> 8)
   1.655 +                        + ((temp[1][k+1] & AG_MASK) >> 8);
   1.656 +                        
   1.657 +                    rb =  (temp[0][k] & RB_MASK)
   1.658 +                        + (temp[1][k] & RB_MASK)
   1.659 +                        + (temp[0][k+1] & RB_MASK)
   1.660 +                        + (temp[1][k+1] & RB_MASK);
   1.661 +
   1.662 +                    /* Shift to divide by 4, adding ½ for rounding */
   1.663 +                    
   1.664 +                    temp[0][k>>1] = ((((ag + 0x00020002u) << 6) & AG_MASK) |
   1.665 +                                     (((rb + 0x00020002u) >> 2) & RB_MASK));
   1.666 +                }
   1.667 +                
   1.668 +#               undef AG_MASK
   1.669 +#               undef RB_MASK
   1.670 +            }
   1.671 +
   1.672 +            /* Write result to the output buffer */
   1.673 +
   1.674 +            convertFromARGB(&temp[0][0], c>>1, format, dst);
   1.675 +            dst += (c>>1) * bpp;
   1.676 +        }
   1.677 +    }
   1.678 +
   1.679 +    /* Return output width and height */
   1.680 +    
   1.681 +    if (w > 1) {
   1.682 +        *pw = (w >> 1);
   1.683 +    }
   1.684 +    if (h > 1) {
   1.685 +        *ph = (h >> 1);
   1.686 +    }
   1.687 +}
   1.688 +
   1.689 +/*!
   1.690 + * \internal
   1.691 + * \brief Returns the OpenGL minification filter corresponding to M3G
   1.692 + * filtering flags
   1.693 + */
   1.694 +static GLenum m3gGetGLMinFilter(M3Genum levelFilter, M3Genum imageFilter) 
   1.695 +{
   1.696 +    static const GLenum minFilter[3][2] = {
   1.697 +        GL_LINEAR, GL_NEAREST,
   1.698 +        GL_LINEAR_MIPMAP_LINEAR, GL_NEAREST_MIPMAP_LINEAR,
   1.699 +        GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_NEAREST
   1.700 +    };
   1.701 +
   1.702 +    return minFilter[levelFilter - M3G_FILTER_BASE_LEVEL][imageFilter - M3G_FILTER_LINEAR];
   1.703 +}
   1.704 +
   1.705 +/*----------------------------------------------------------------------
   1.706 + * Internal functions
   1.707 + *--------------------------------------------------------------------*/
   1.708 +
   1.709 +/*!
   1.710 + * \internal
   1.711 + * \brief Converts an internal ARGB color to four GLfixed components
   1.712 + */
   1.713 +static void m3gGLColor(M3Guint argb, GLfixed *dst)
   1.714 +{
   1.715 +    GLfixed r, g, b, a;
   1.716 +        
   1.717 +    r = (GLfixed)((argb & 0x00FF0000u) >> 16);
   1.718 +    g = (GLfixed)((argb & 0x0000FF00u) >>  8);
   1.719 +    b = (GLfixed)( argb & 0x000000FFu       );
   1.720 +    a = (GLfixed)((argb & 0xFF000000u) >> 24);
   1.721 +
   1.722 +    dst[0] = ((r << 8) | r) + (r >> 7);
   1.723 +    dst[1] = ((g << 8) | g) + (g >> 7);
   1.724 +    dst[2] = ((b << 8) | b) + (b >> 7);
   1.725 +    dst[3] = ((a << 8) | a) + (a >> 7);
   1.726 +}
   1.727 +
   1.728 +/*!
   1.729 + * \internal
   1.730 + * \brief Binds an image into the current texture unit and sets up
   1.731 + * texture filtering
   1.732 + */
   1.733 +static void m3gBindTextureImage(Image *img, M3Genum levelFilter, M3Genum imageFilter)
   1.734 +{
   1.735 +    M3G_ASSERT_GL;
   1.736 +    
   1.737 +    /* We have no mipmap generation for paletted images, so disable
   1.738 +     * mipmapping in that case */
   1.739 +    
   1.740 +    if (m3gIsInternallyPaletted(img)) {
   1.741 +        levelFilter = M3G_FILTER_BASE_LEVEL;
   1.742 +    }
   1.743 +
   1.744 +    /* Bind the OpenGL texture object, generating mipmaps if
   1.745 +     * required */
   1.746 +    
   1.747 +    m3gBindTextureObject(img, levelFilter != M3G_FILTER_BASE_LEVEL);
   1.748 +
   1.749 +    /* Set up OpenGL texture filtering according to our flags */
   1.750 +
   1.751 +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
   1.752 +                    (imageFilter == M3G_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST);
   1.753 +    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
   1.754 +                    m3gGetGLMinFilter(levelFilter, imageFilter));
   1.755 +    
   1.756 +    M3G_ASSERT_GL;
   1.757 +}
   1.758 +
   1.759 +/*!
   1.760 + * \internal
   1.761 + * \brief Maps a logical image format to the matching default pixel
   1.762 + * format
   1.763 + * 
   1.764 + * \param imgFormat logical image format
   1.765 + * \return a one-byte-per-pixel pixel format
   1.766 + */
   1.767 +static M3GPixelFormat m3gPixelFormat(M3GImageFormat imgFormat)
   1.768 +{
   1.769 +    switch (imgFormat) {
   1.770 +    case M3G_ALPHA:
   1.771 +        return M3G_A8;
   1.772 +    case M3G_LUMINANCE:
   1.773 +        return M3G_L8;
   1.774 +    case M3G_LUMINANCE_ALPHA:
   1.775 +        return M3G_LA8;
   1.776 +    case M3G_RGB:
   1.777 +        return M3G_RGB8;
   1.778 +    case M3G_RGBA:
   1.779 +        return M3G_RGBA8;
   1.780 +    default:
   1.781 +        M3G_ASSERT(M3G_FALSE);
   1.782 +        return M3G_NO_FORMAT;
   1.783 +    }
   1.784 +}
   1.785 +
   1.786 +/*!
   1.787 + * \internal
   1.788 + * \brief Returns the number of bytes per pixel in a given pixel format
   1.789 + *
   1.790 + * \param format pixel format
   1.791 + * \return bytes per pixel
   1.792 + */
   1.793 +static M3Gint m3gBytesPerPixel(M3GPixelFormat format)
   1.794 +{
   1.795 +    switch (format) {
   1.796 +    case M3G_L8:
   1.797 +    case M3G_A8:
   1.798 +    case M3G_LA4:
   1.799 +    case M3G_PALETTE8_RGB8:
   1.800 +    case M3G_PALETTE8_RGB8_32:
   1.801 +    case M3G_PALETTE8_RGBA8:
   1.802 +        return 1;
   1.803 +    case M3G_RGB4:
   1.804 +    case M3G_RGB565:
   1.805 +    case M3G_RGBA4:
   1.806 +    case M3G_RGB5A1:
   1.807 +    case M3G_LA8:
   1.808 +        return 2;
   1.809 +    case M3G_RGB8:
   1.810 +        return 3;
   1.811 +    case M3G_RGBA8:
   1.812 +    case M3G_BGRA8:
   1.813 +    case M3G_ARGB8:
   1.814 +    case M3G_BGR8_32:
   1.815 +    case M3G_RGB8_32:
   1.816 +        return 4;
   1.817 +    default:
   1.818 +        M3G_ASSERT(M3G_FALSE);
   1.819 +        return 0;
   1.820 +    }
   1.821 +}
   1.822 +
   1.823 +/*!
   1.824 + * \internal
   1.825 + * \brief Converts pixels between formats
   1.826 + *
   1.827 + * \note Only a limited subset of source and destination formats may
   1.828 + * be supported; see the \c convert functions in m3g_image.c
   1.829 + *
   1.830 + * \param srcFormat source format
   1.831 + * \param src       source pixels
   1.832 + * \param dstFormat destination format
   1.833 + * \param dst       destination pixels
   1.834 + * \param count     pixel count
   1.835 + */
   1.836 +static void m3gConvertPixels(M3GPixelFormat srcFormat, const M3Gubyte *src,
   1.837 +                             M3GPixelFormat dstFormat, M3Gubyte *dst,
   1.838 +                             M3Gsizei count)
   1.839 +{
   1.840 +    M3Guint temp[SPAN_BUFFER_SIZE];
   1.841 +    const char endianTest[4] = { 1, 0, 0, 0 };
   1.842 +
   1.843 +    M3Guint srcBpp = m3gBytesPerPixel(srcFormat);
   1.844 +    M3Guint dstBpp = m3gBytesPerPixel(dstFormat);
   1.845 +    M3G_ASSERT(srcBpp > 0 && dstBpp > 0);
   1.846 +
   1.847 +    while (count > 0) {
   1.848 +        M3Gsizei n = count;
   1.849 +
   1.850 +        /* Check the source and destination formats to avoid 
   1.851 +           the intermediate ARGB format conversion. */
   1.852 +        if (((srcFormat == M3G_RGBA8 && (dstFormat == M3G_BGRA8 || dstFormat == M3G_BGR8_32))
   1.853 +            || (dstFormat == M3G_RGBA8 && (srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32))) 
   1.854 +            && (n > 2) && ((*(const int *)endianTest) == 1)) {
   1.855 +            /* use fast path for RGBA<->BGRA conversion */
   1.856 +            fastConvertBGRAToRGBA(src, n * srcBpp, n, 1, dst);
   1.857 +        } else if (srcFormat == M3G_ARGB8 && dstFormat != M3G_ARGB8) {
   1.858 +            convertFromARGB((M3Guint*)src, n, dstFormat, dst);
   1.859 +        } else if (srcFormat != M3G_ARGB8 && dstFormat == M3G_ARGB8) {
   1.860 +            convertToARGB(srcFormat, src, n, (M3Guint*)dst);
   1.861 +        } else {
   1.862 +            /* no luck, do the conversion via ARGB (source format -> ARGB -> destination format) */
   1.863 +            n = (count < SPAN_BUFFER_SIZE) ? count : SPAN_BUFFER_SIZE;
   1.864 +            convertToARGB(srcFormat, src, n, temp);
   1.865 +            convertFromARGB(temp, n, dstFormat, dst);
   1.866 +        }
   1.867 +        count -= n;
   1.868 +        src += n * srcBpp;
   1.869 +        dst += n * dstBpp;
   1.870 +    }
   1.871 +}
   1.872 +
   1.873 +/*!
   1.874 + * \internal
   1.875 + * \brief Copies image data. The source image is copied to
   1.876 + * the destination image.
   1.877 + *
   1.878 + * \param dst destination image
   1.879 + * \param src source image
   1.880 + */
   1.881 +static void m3gCopyImagePixels(Image *dst,
   1.882 +                               const Image *src)
   1.883 +{
   1.884 +    const M3Gubyte *pSrc;
   1.885 +    M3Gubyte *pDst;
   1.886 +    M3Gint bpp;
   1.887 +
   1.888 +    /* Check inputs (debug only!) */
   1.889 +    M3G_VALIDATE_OBJECT(dst);
   1.890 +    M3G_VALIDATE_OBJECT(src);
   1.891 +
   1.892 +    M3G_ASSERT(src->internalFormat == dst->internalFormat);
   1.893 +    M3G_ASSERT(src->format == dst->format);
   1.894 +
   1.895 +    M3G_ASSERT(src->paletteBytes == dst->paletteBytes);
   1.896 +    
   1.897 +    /* Compute source and destination pixel data pointers */
   1.898 +    pSrc = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(src), src->data);
   1.899 +    pDst = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(dst), dst->data);
   1.900 +
   1.901 +    bpp = m3gBytesPerPixel(src->internalFormat);
   1.902 +
   1.903 +    if (src->paletteBytes > 0) {
   1.904 +        m3gCopy(pDst, pSrc, src->paletteBytes);
   1.905 +        pDst += dst->paletteBytes;
   1.906 +        pSrc += src->paletteBytes;
   1.907 +    }
   1.908 +
   1.909 +    /* Do a straight copy if the sizes match, or resample if not */
   1.910 +    if (src->width == dst->width && src->height == dst->height ) {
   1.911 +        m3gCopy(pDst, pSrc, src->width * src->height * bpp);
   1.912 +    }
   1.913 +    else {
   1.914 +        /* Adder values as 8.8 fixed point */
   1.915 +        M3Gint xAdd, yAdd;
   1.916 +        M3Gint x, y;
   1.917 +
   1.918 +        xAdd = (256 * src->width) / dst->width;
   1.919 +        yAdd = (256 * src->height) / dst->height;
   1.920 +
   1.921 +        for (y = 0; y < dst->height; y++) {
   1.922 +            for (x = 0; x < dst->width; x++) {
   1.923 +                m3gCopy(pDst, pSrc + bpp * (((xAdd * x) >> 8) + ((yAdd * y) >> 8) * src->width), bpp);
   1.924 +                pDst += bpp;
   1.925 +            }
   1.926 +        }
   1.927 +    }
   1.928 +
   1.929 +    m3gUnmapObject(M3G_INTERFACE(dst), dst->data);
   1.930 +    m3gUnmapObject(M3G_INTERFACE(src), src->data);
   1.931 +
   1.932 +    m3gInvalidateImage(dst);
   1.933 +}
   1.934 +
   1.935 +/*!
   1.936 + * \internal
   1.937 + * \brief Invalidates any cached data for this image
   1.938 + *
   1.939 + * Used when rendering to the image.
   1.940 + *
   1.941 + * \param img Image object
   1.942 + */
   1.943 +static void m3gInvalidateImage(Image *img)
   1.944 +{
   1.945 +    M3G_VALIDATE_OBJECT(img);
   1.946 +    img->dirty = M3G_TRUE;
   1.947 +    
   1.948 +#   if !defined(M3G_NGL_TEXTURE_API)
   1.949 +    if (img->large) {
   1.950 +        img->large->dirty = M3G_TRUE;
   1.951 +    }
   1.952 +#   endif /*M3G_NGL_TEXTURE_API*/
   1.953 +
   1.954 +    if (img->powerOfTwo != img) {
   1.955 +        img->powerOfTwoDirty = M3G_TRUE;
   1.956 +    }
   1.957 +}
   1.958 +
   1.959 +/*!
   1.960 + * \internal
   1.961 + * \brief Overloaded Object3D method.
   1.962 + *
   1.963 + * \param originalObj original Image object
   1.964 + * \param cloneObj pointer to cloned Image object
   1.965 + * \param pairs array for all object-duplicate pairs
   1.966 + * \param numPairs number of pairs
   1.967 + */
   1.968 +static M3Gbool m3gImageDuplicate(const Object *originalObj,
   1.969 +                                 Object **cloneObj,
   1.970 +                                 Object **pairs,
   1.971 +                                 M3Gint *numPairs)
   1.972 +{
   1.973 +    Image *original = (Image *)originalObj;
   1.974 +    Image *clone;
   1.975 +
   1.976 +    /* If the original image still has its pixel data, make a full
   1.977 +     * copy -- this is wasteful for immutable images, but the shame's
   1.978 +     * on the user in that case */
   1.979 +    
   1.980 +    if (original->data) {
   1.981 +        clone = (Image*) m3gCreateImage(originalObj->interface,
   1.982 +                                        original->format,
   1.983 +                                        original->width,
   1.984 +                                        original->height,
   1.985 +                                        original->flags);
   1.986 +    }
   1.987 +    else {
   1.988 +
   1.989 +        /* Otherwise, just point to the original and use its data
   1.990 +         * buffers */
   1.991 +        
   1.992 +        clone = (Image*) m3gAlloc(M3G_INTERFACE(original), sizeof(*clone));
   1.993 +        *clone = *original;
   1.994 +        M3G_ASSIGN_REF(clone->copyOf, original);
   1.995 +    }
   1.996 +    
   1.997 +    *cloneObj = (Object *)clone;
   1.998 +    if (*cloneObj == NULL) {
   1.999 +        return M3G_FALSE;
  1.1000 +    }
  1.1001 +
  1.1002 +    if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
  1.1003 +        /* Copy image contents */
  1.1004 +        if (original->data) {
  1.1005 +            m3gCopyImagePixels(clone, original);
  1.1006 +        }
  1.1007 +        return M3G_TRUE;
  1.1008 +    }
  1.1009 +    else {
  1.1010 +        return M3G_FALSE;
  1.1011 +    }
  1.1012 +}
  1.1013 +
  1.1014 +/*!
  1.1015 + * \internal
  1.1016 + *
  1.1017 + * \brief Frees the pixel data associated with this image; used for
  1.1018 + * optimizing memory usage after copying the data to a secondary
  1.1019 + * location
  1.1020 + */
  1.1021 +static void m3gFreeImageData(Image *img)
  1.1022 +{
  1.1023 +    M3G_ASSERT(img->format == M3G_RGB || img->format == M3G_LUMINANCE);
  1.1024 +#   if !defined(M3G_NGL_TEXTURE_API)
  1.1025 +    M3G_ASSERT(!img->mipmapsDirty);
  1.1026 +#   endif
  1.1027 +    M3G_ASSERT(!img->powerOfTwoDirty);
  1.1028 +    M3G_ASSERT(img->powerOfTwo != NULL);
  1.1029 +    M3G_ASSERT(!img->pinned);
  1.1030 +    
  1.1031 +    M3G_LOG1(M3G_LOG_IMAGES, "Freeing copy of image 0x%08X\n",
  1.1032 +             (unsigned) img);
  1.1033 +
  1.1034 +    if (!img->copyOf) {
  1.1035 +        m3gFreeObject(M3G_INTERFACE(img), img->data);
  1.1036 +        img->data = 0;
  1.1037 +        m3gFreeObject(M3G_INTERFACE(img), img->mipData);
  1.1038 +        img->mipData = 0;
  1.1039 +    }
  1.1040 +    M3G_ASSIGN_REF(img->copyOf, NULL);
  1.1041 +}
  1.1042 +
  1.1043 +/*!
  1.1044 + * \internal
  1.1045 + * \brief Returns a power-of-two variant of an image
  1.1046 + *
  1.1047 + * This is used for sprites and background images.
  1.1048 + */
  1.1049 +static Image *m3gGetPowerOfTwoImage(Image *img)
  1.1050 +{
  1.1051 +    M3G_VALIDATE_OBJECT(img);
  1.1052 +    
  1.1053 +    /* Create a power-of-two variant of the image if one doesn't exist
  1.1054 +     * already */
  1.1055 +    
  1.1056 +    if (img->powerOfTwo == NULL) {
  1.1057 +
  1.1058 +        M3Gint width, height;
  1.1059 +        M3Gbitmask flags;
  1.1060 +        Image *potImage;
  1.1061 +        
  1.1062 +        M3G_ASSERT(!m3gIsPowerOfTwo(img->width) ||
  1.1063 +                   !m3gIsPowerOfTwo(img->height));
  1.1064 +        
  1.1065 +        /* Choose new image size to allow a maximum shrinkage of 25%;
  1.1066 +         * this is to weed out pathological cases of quadruple memory
  1.1067 +         * usage because an image is one pixel too wide */
  1.1068 +
  1.1069 +        width  = m3gNextPowerOfTwo((img->width * 3) >> 2);
  1.1070 +        height = m3gNextPowerOfTwo((img->height * 3) >> 2);
  1.1071 +        
  1.1072 +        width  = M3G_MIN(width, M3G_MAX_TEXTURE_DIMENSION);
  1.1073 +        height = M3G_MIN(height, M3G_MAX_TEXTURE_DIMENSION);
  1.1074 +        
  1.1075 +        flags = img->flags & (~M3G_RENDERING_TARGET);
  1.1076 +        
  1.1077 +        potImage = m3gCreateImage(M3G_INTERFACE(img),
  1.1078 +                                  img->format,
  1.1079 +                                  width, height,
  1.1080 +                                  flags);
  1.1081 +        if (!potImage) {
  1.1082 +            return NULL; /* automatic out-of-memory */
  1.1083 +        }
  1.1084 +
  1.1085 +        M3G_ASSIGN_REF(img->powerOfTwo, potImage);
  1.1086 +        img->powerOfTwoDirty = M3G_TRUE;
  1.1087 +    }
  1.1088 +
  1.1089 +    /* Update POT image data if necessary */
  1.1090 +    
  1.1091 +    if (img->powerOfTwoDirty) {
  1.1092 +        m3gCopyImagePixels(img->powerOfTwo, img);
  1.1093 +        img->powerOfTwoDirty = M3G_FALSE;
  1.1094 +
  1.1095 +        /* Get rid of the original at this point if we can */
  1.1096 +        
  1.1097 +        if (!img->pinned) {
  1.1098 +            m3gFreeImageData(img);
  1.1099 +        }
  1.1100 +    }
  1.1101 +
  1.1102 +    return img->powerOfTwo;
  1.1103 +}
  1.1104 +
  1.1105 +/*!
  1.1106 + * \internal
  1.1107 + * \brief Gets image alpha at x, y.
  1.1108 + *
  1.1109 + * \param image Image object
  1.1110 + * \param x x-coordinate
  1.1111 + * \param y y-coordinate
  1.1112 + * \return alpha value
  1.1113 + *
  1.1114 + */
  1.1115 +static M3Gint m3gGetAlpha(Image *image, M3Gint x, M3Gint y)
  1.1116 +{
  1.1117 +    M3Gint alpha = 255;
  1.1118 +    M3Gint bpp = m3gBytesPerPixel(image->internalFormat);
  1.1119 +    M3Guint data = 0;
  1.1120 +    M3Gubyte *pixels;
  1.1121 +
  1.1122 +    /* Quick exit for non-alpha formats */
  1.1123 +    
  1.1124 +    if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
  1.1125 +        return alpha;
  1.1126 +    }
  1.1127 +
  1.1128 +    /* For other formats, we have to sample the image data */
  1.1129 +
  1.1130 +    if (!image->data) {
  1.1131 +        Image *potImage = image->powerOfTwo;
  1.1132 +        M3G_ASSERT(potImage != image);
  1.1133 +        return m3gGetAlpha(potImage,
  1.1134 +                           (x * image->width) / potImage->width,
  1.1135 +                           (y * image->height) / potImage->height);
  1.1136 +    }
  1.1137 +    
  1.1138 +    pixels = ((M3Gubyte *)m3gMapObject(M3G_INTERFACE(image), image->data));
  1.1139 +
  1.1140 +    if (image->paletteBytes == 0) {
  1.1141 +        if (bpp == 1) {
  1.1142 +            data = pixels[x + y * image->width];
  1.1143 +        }
  1.1144 +        else if (bpp == 2) {
  1.1145 +            data = ((M3Gushort *)pixels)[x + y * image->width];
  1.1146 +        }
  1.1147 +        else {
  1.1148 +            data = ((M3Guint *)pixels)[x + y * image->width];
  1.1149 +        }
  1.1150 +    }
  1.1151 +    else {
  1.1152 +        M3Guint *palette;
  1.1153 +        palette = (M3Guint *)pixels;
  1.1154 +        pixels += image->paletteBytes;
  1.1155 +
  1.1156 +        data = palette[pixels[x + y * image->width]];
  1.1157 +    }
  1.1158 +
  1.1159 +    m3gUnmapObject(M3G_INTERFACE(image), image->data);
  1.1160 +
  1.1161 +    switch (image->internalFormat) {
  1.1162 +
  1.1163 +    case M3G_A8:
  1.1164 +        alpha = data;
  1.1165 +        break;
  1.1166 +    case M3G_LA8:
  1.1167 +        alpha = data >> 8;
  1.1168 +        break;
  1.1169 +    case M3G_RGBA8:
  1.1170 +        alpha = data >> 24;
  1.1171 +        break;
  1.1172 +    default:
  1.1173 +        /* Should never be here!! */
  1.1174 +        M3G_ASSERT(M3G_FALSE);
  1.1175 +    }
  1.1176 +
  1.1177 +    return alpha;
  1.1178 +}
  1.1179 +
  1.1180 +/*!
  1.1181 + * \internal
  1.1182 + * \brief Computes the scanline stride of an image
  1.1183 + */
  1.1184 +static M3Gsizei m3gGetImageStride(const Image *img)
  1.1185 +{
  1.1186 +    M3G_VALIDATE_OBJECT(img);
  1.1187 +    return img->width * m3gBytesPerPixel(img->internalFormat);
  1.1188 +}
  1.1189 +
  1.1190 +/*----------------------------------------------------------------------
  1.1191 + * Virtual function table
  1.1192 + *--------------------------------------------------------------------*/
  1.1193 +
  1.1194 +static const ObjectVFTable m3gvf_Image = {
  1.1195 +    m3gObjectApplyAnimation,
  1.1196 +    m3gObjectIsCompatible,
  1.1197 +    m3gObjectUpdateProperty,
  1.1198 +    m3gObjectDoGetReferences,
  1.1199 +    m3gObjectFindID,
  1.1200 +    m3gImageDuplicate,
  1.1201 +    m3gDestroyImage
  1.1202 +};
  1.1203 +
  1.1204 +
  1.1205 +/*----------------------------------------------------------------------
  1.1206 + * Public API functions
  1.1207 + *--------------------------------------------------------------------*/
  1.1208 +
  1.1209 +/*!
  1.1210 + * \brief Creates a new Image
  1.1211 + *
  1.1212 + * \param interface     M3G interface
  1.1213 + * \param srcFormat     source format
  1.1214 + * \param width         width in pixels
  1.1215 + * \param height        height in pixels
  1.1216 + * \param flags         creation flags; a combination of
  1.1217 + *                      M3G_DYNAMIC, M3G_STATIC,
  1.1218 + *                      M3G_RENDERING_TARGET, and M3G_PALETTED
  1.1219 + * \retval Image new Image object
  1.1220 + * \retval NULL Image creating failed
  1.1221 + */
  1.1222 +M3G_API M3GImage m3gCreateImage(/*@dependent@*/ M3GInterface interface,
  1.1223 +                                M3GImageFormat srcFormat,
  1.1224 +                                M3Gint width, M3Gint height,
  1.1225 +                                M3Gbitmask flags)
  1.1226 +{
  1.1227 +    Interface *m3g = (Interface *) interface;
  1.1228 +    M3G_VALIDATE_INTERFACE(m3g);
  1.1229 +    
  1.1230 +    /* Check errors */
  1.1231 +    
  1.1232 +    if (width <= 0 || height <= 0) {
  1.1233 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1234 +        return NULL;
  1.1235 +    }
  1.1236 +
  1.1237 +    if (!m3gInRange(srcFormat, M3G_ALPHA, M3G_RGBA)) {
  1.1238 +        m3gRaiseError(m3g, M3G_INVALID_ENUM);
  1.1239 +        return NULL;
  1.1240 +    }
  1.1241 +
  1.1242 +    /* Parameters OK; allocate and initialize the object */
  1.1243 +
  1.1244 +    {
  1.1245 +        Image *img = m3gAllocZ(m3g, sizeof(Image));
  1.1246 +        if (img == NULL) {
  1.1247 +            return NULL;
  1.1248 +        }
  1.1249 +
  1.1250 +        /* Clean up and set flags */
  1.1251 +
  1.1252 +        M3G_LOG3(M3G_LOG_IMAGES, "Image 0x%08X is %d x %d",
  1.1253 +                 (unsigned) img, width, height);
  1.1254 +        
  1.1255 +        flags |= M3G_DYNAMIC;   /* the default */
  1.1256 +        
  1.1257 +        if (flags & M3G_STATIC) {
  1.1258 +            M3G_LOG(M3G_LOG_IMAGES, ", immutable");
  1.1259 +            flags &= ~M3G_DYNAMIC;
  1.1260 +        }
  1.1261 +        if (flags & M3G_RENDERING_TARGET) {
  1.1262 +            M3G_LOG(M3G_LOG_IMAGES, ", rendertarget");
  1.1263 +            flags |= M3G_DYNAMIC;
  1.1264 +        }
  1.1265 +        if (flags & M3G_PALETTED) {
  1.1266 +            M3G_LOG(M3G_LOG_IMAGES, ", paletted");
  1.1267 +        }
  1.1268 +        img->flags = flags;
  1.1269 +
  1.1270 +        M3G_LOG(M3G_LOG_IMAGES, "\n");
  1.1271 +        
  1.1272 +        {
  1.1273 +            /* Allocate pixel & palette data; the palette is stored at
  1.1274 +             * the beginning of the pixel data chunk */
  1.1275 +
  1.1276 +            M3Gbool paletted = ((img->flags & M3G_PALETTED) != 0)
  1.1277 +                && m3gSupportedPaletteFormat(srcFormat);
  1.1278 +            M3GPixelFormat internalFormat = getInternalFormat(srcFormat,
  1.1279 +                                                              paletted);
  1.1280 +            M3Guint bpp = m3gBytesPerPixel(internalFormat);
  1.1281 +            M3Guint pixelBytes = width * height * bpp;
  1.1282 +
  1.1283 +            if ((img->flags & M3G_PALETTED) != 0 && !paletted) {
  1.1284 +                M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_IMAGES,
  1.1285 +                        "Warning: Unsupported paletted format\n");
  1.1286 +            }
  1.1287 +                
  1.1288 +            /* The palette will always have 256 elements and one byte
  1.1289 +             * per color component (except padded 32-bit for NGL) */
  1.1290 +            
  1.1291 +            if (paletted) {
  1.1292 +                img->paletteBytes =
  1.1293 +#                   if defined(M3G_NGL_TEXTURE_API)
  1.1294 +                    256 * 4;
  1.1295 +#                   else
  1.1296 +                    256 * m3gBytesPerPixel(m3gPixelFormat(srcFormat));
  1.1297 +#                   endif
  1.1298 +            }
  1.1299 +
  1.1300 +            /* Set up the rest of the image parameters */
  1.1301 +            
  1.1302 +            img->width = width;
  1.1303 +            img->height = height;
  1.1304 +            img->format = srcFormat;
  1.1305 +            img->internalFormat = internalFormat;
  1.1306 +            img->glFormat = m3gGetGLFormat(internalFormat);
  1.1307 +
  1.1308 +            M3G_LOG1(M3G_LOG_IMAGES, "Image data %d bytes\n",
  1.1309 +                     pixelBytes + img->paletteBytes);
  1.1310 +            
  1.1311 +            /* Allocate the image memory */
  1.1312 +            
  1.1313 +            img->data = m3gAllocObject(m3g, pixelBytes + img->paletteBytes);
  1.1314 +            if (img->data == 0) {
  1.1315 +                m3gFree(m3g, img);
  1.1316 +                return NULL;
  1.1317 +            }
  1.1318 +
  1.1319 +#ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
  1.1320 +            /* If GLES resource freeing (see function m3gFreeGLESResources) 
  1.1321 +               is enabled, the GL texture might get deleted at any point, so
  1.1322 +			   a copy of the texture data has to be always kept in memory. */
  1.1323 +            img->pinned = M3G_TRUE;
  1.1324 +#else           
  1.1325 +            /* Lock the image data in memory if the image is dynamic,
  1.1326 +             * or the format has alpha information; otherwise, we'll
  1.1327 +             * be able to get rid of an extra copy when generating a
  1.1328 +             * power-of-two version or uploading to OpenGL */
  1.1329 +            
  1.1330 +            if ((img->flags & M3G_DYNAMIC) != 0
  1.1331 +                || (img->format != M3G_RGB &&
  1.1332 +                    img->format != M3G_LUMINANCE)) {
  1.1333 +                img->pinned = M3G_TRUE;
  1.1334 +            }
  1.1335 +#endif
  1.1336 +            /* If the image can be used as a rendering target, clear
  1.1337 +             * to opaque white by default */
  1.1338 +            
  1.1339 +            if ((img->flags & M3G_RENDERING_TARGET) != 0) {
  1.1340 +                M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data))
  1.1341 +                    + img->paletteBytes;
  1.1342 +                m3gFill(pixels, (size_t) pixelBytes, -1); 
  1.1343 +                m3gUnmapObject(m3g, img->data);
  1.1344 +            }
  1.1345 +
  1.1346 +            /* Check for "special" images that can't be used as
  1.1347 +             * textures without some extra trickery */
  1.1348 +
  1.1349 +            if (!m3gIsPowerOfTwo((M3Guint) width) ||
  1.1350 +                !m3gIsPowerOfTwo((M3Guint) height)) {
  1.1351 +                img->special |= IMG_NPOT;
  1.1352 +            }
  1.1353 +            else {
  1.1354 +                img->powerOfTwo = img;
  1.1355 +            }
  1.1356 +            
  1.1357 +            if (width > M3G_MAX_TEXTURE_DIMENSION ||
  1.1358 +                height > M3G_MAX_TEXTURE_DIMENSION) {
  1.1359 +                img->special |= IMG_LARGE;
  1.1360 +            }
  1.1361 +        }
  1.1362 +
  1.1363 +        /* Call base class constructor (can not fail) and return */
  1.1364 +        m3gInitObject(&img->object, m3g, M3G_CLASS_IMAGE);
  1.1365 +
  1.1366 +        M3G_VALIDATE_OBJECT(img);
  1.1367 +        return (M3GImage) img;
  1.1368 +    }
  1.1369 +}
  1.1370 +
  1.1371 +/*!
  1.1372 + * \brief Prevents further modifications to an image
  1.1373 + *
  1.1374 + * Essentially, this changes the default M3G_DYNAMIC flag to
  1.1375 + * M3G_STATIC; this allows the implementation to make memory and
  1.1376 + * performance optimizations not possible for dynamically modified
  1.1377 + * images.
  1.1378 + */
  1.1379 +M3G_API void m3gCommitImage(M3GImage hImage)
  1.1380 +{
  1.1381 +    Image *image = (Image *) hImage;
  1.1382 +    M3Gbitmask flags;
  1.1383 +    M3G_VALIDATE_OBJECT(image);
  1.1384 +    
  1.1385 +    flags = image->flags;
  1.1386 +    flags &= ~(M3G_DYNAMIC|M3G_RENDERING_TARGET);
  1.1387 +    flags |= M3G_STATIC;
  1.1388 +    
  1.1389 +    image->flags = flags;
  1.1390 +
  1.1391 +#ifndef M3G_ENABLE_GLES_RESOURCE_HANDLING
  1.1392 +    /* If the image format has no alpha information, we can discard
  1.1393 +     * the image data under suitable conditions */
  1.1394 +    
  1.1395 +    if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
  1.1396 +        image->pinned = M3G_FALSE;
  1.1397 +    }
  1.1398 +#endif    
  1.1399 +    M3G_LOG1(M3G_LOG_IMAGES, "Image 0x%08X made immutable\n",
  1.1400 +             (unsigned) image);
  1.1401 +}
  1.1402 +
  1.1403 +/*!
  1.1404 + * \brief Check if image is mutable.
  1.1405 + * 
  1.1406 + * \param hImage Image object
  1.1407 + * \retval M3G_TRUE image is mutable
  1.1408 + * \retval M3G_FALSE image is immutable
  1.1409 + */
  1.1410 +M3G_API M3Gbool m3gIsMutable(M3GImage hImage)
  1.1411 +{
  1.1412 +    Image *image = (Image *) hImage;
  1.1413 +    M3G_VALIDATE_OBJECT(image);
  1.1414 +    return ((image->flags & M3G_DYNAMIC) != 0);
  1.1415 +}
  1.1416 +
  1.1417 +/*!
  1.1418 + * \brief Gets image format as JSR-184 constant
  1.1419 + * 
  1.1420 + * \param hImage Image object
  1.1421 + * \return JSR-184 format
  1.1422 + */
  1.1423 +M3G_API M3GImageFormat m3gGetFormat(M3GImage hImage)
  1.1424 +{
  1.1425 +    Image *image = (Image *) hImage;
  1.1426 +    M3G_VALIDATE_OBJECT(image);
  1.1427 +    return image->format;
  1.1428 +}
  1.1429 +
  1.1430 +/*!
  1.1431 + * \brief Gets image width
  1.1432 + * 
  1.1433 + * \param hImage Image object
  1.1434 + * \return width in pixels
  1.1435 + */
  1.1436 +M3G_API M3Gint m3gGetWidth(M3GImage hImage)
  1.1437 +{
  1.1438 +    Image *image = (Image *) hImage;
  1.1439 +    M3G_VALIDATE_OBJECT(image);
  1.1440 +    return image->width;
  1.1441 +}
  1.1442 +
  1.1443 +/*!
  1.1444 + * \brief Gets image height
  1.1445 + * 
  1.1446 + * \param hImage Image object
  1.1447 + * \return height in pixels
  1.1448 + */
  1.1449 +M3G_API M3Gint m3gGetHeight(M3GImage hImage)
  1.1450 +{
  1.1451 +    Image *image = (Image *) hImage;
  1.1452 +    M3G_VALIDATE_OBJECT(image);
  1.1453 +    return image->height;
  1.1454 +}
  1.1455 +
  1.1456 +/*!
  1.1457 + * \brief Converts a rectangle of pixels of src to dst as srcFormat to
  1.1458 + * dstFormat conversion requires.
  1.1459 + *
  1.1460 + * \param srcFormat source format
  1.1461 + * \param src       source pixels
  1.1462 + * \param srcStride source stride
  1.1463 + * \param width     width in pixels
  1.1464 + * \param height    height in pixels
  1.1465 + * \param dstFormat destination format
  1.1466 + * \param dst       destination pixels
  1.1467 + * \param dstStride destination stride
  1.1468 + */
  1.1469 +static void m3gConvertPixelRect(
  1.1470 +    M3GPixelFormat srcFormat, const M3Gubyte *src, M3Gsizei srcStride,
  1.1471 +    M3Gsizei width, M3Gsizei height,
  1.1472 +    M3GPixelFormat dstFormat, M3Gubyte *dst, M3Gsizei dstStride)
  1.1473 +{
  1.1474 +    /* Detect any fast path cases */
  1.1475 +    
  1.1476 +    if ((srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32)
  1.1477 +        && dstFormat == M3G_RGBA8) {
  1.1478 +        if (width > 2 && dstStride == width*4) {
  1.1479 +
  1.1480 +            const char endianTest[4] = { 1, 0, 0, 0 };
  1.1481 +            if ((*(const int *)endianTest) == 1) {
  1.1482 +                fastConvertBGRAToRGBA(src, srcStride, width, height, dst);
  1.1483 +            }
  1.1484 +            return;
  1.1485 +        }
  1.1486 +    }
  1.1487 +
  1.1488 +    /* No luck, do the generic conversion */
  1.1489 +        
  1.1490 +    while (height-- > 0) {
  1.1491 +        m3gConvertPixels(srcFormat, src, dstFormat, dst, width);
  1.1492 +        src += srcStride;
  1.1493 +        dst += dstStride;
  1.1494 +    }
  1.1495 +}
  1.1496 +
  1.1497 +/*!
  1.1498 + * \brief Sets the pixel data for an image
  1.1499 + * 
  1.1500 + * \param hImage Image object
  1.1501 + * \param srcPixels source pixels
  1.1502 + */
  1.1503 +M3G_API void m3gSetImage(M3GImage hImage, const void *srcPixels)
  1.1504 +{
  1.1505 +    Image *img = (Image *) hImage;
  1.1506 +    M3G_VALIDATE_OBJECT(img);
  1.1507 +
  1.1508 +    {
  1.1509 +        M3Gsizei bpp = m3gBytesPerPixel(m3gInputDataFormat(img));
  1.1510 +        m3gSetSubImage(hImage,
  1.1511 +                       0, 0, img->width, img->height,
  1.1512 +                       img->width * img->height * bpp, srcPixels);
  1.1513 +    }
  1.1514 +}
  1.1515 +
  1.1516 +/*!
  1.1517 + * \brief Reads pixel data from an image
  1.1518 + *
  1.1519 + * \param hImage Image object
  1.1520 + * \param pixels output buffer for pixels
  1.1521 + */
  1.1522 +M3G_API void m3gGetImageARGB(M3GImage hImage, M3Guint *pixels)
  1.1523 +{
  1.1524 +    Interface *m3g;
  1.1525 +    const Image *img = (const Image *) hImage;
  1.1526 +    M3G_VALIDATE_OBJECT(img);
  1.1527 +    m3g = M3G_INTERFACE(img);
  1.1528 +    
  1.1529 +    if (!pixels) {
  1.1530 +        m3gRaiseError(m3g, M3G_NULL_POINTER);
  1.1531 +        return;
  1.1532 +    }
  1.1533 +    
  1.1534 +    if (img->data) {
  1.1535 +        const M3Gubyte *src = (const M3Gubyte*) m3gMapObject(m3g, img->data);
  1.1536 +        convertToARGB(img->internalFormat, src,
  1.1537 +                      img->width * img->height,
  1.1538 +                      pixels);
  1.1539 +        m3gUnmapObject(m3g, img->data);
  1.1540 +    }
  1.1541 +}
  1.1542 +
  1.1543 +/*!
  1.1544 + * \brief Sets the palette for an image
  1.1545 + * 
  1.1546 + * \param hImage Image object
  1.1547 + * \param paletteLength length of the palette
  1.1548 + * \param srcPalette palette data
  1.1549 + */
  1.1550 +M3G_API void m3gSetImagePalette(M3GImage hImage,
  1.1551 +                                M3Gint paletteLength,
  1.1552 +                                const void *srcPalette)
  1.1553 +{
  1.1554 +    Interface *m3g;
  1.1555 +    Image *img = (Image *) hImage;
  1.1556 +    M3G_VALIDATE_OBJECT(img);
  1.1557 +    m3g = M3G_INTERFACE(img);
  1.1558 +
  1.1559 +    /* Check for errors */
  1.1560 +
  1.1561 +    if (img->data == 0 || (img->flags & M3G_STATIC) != 0
  1.1562 +            || (img->flags & M3G_PALETTED) == 0) {
  1.1563 +        M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
  1.1564 +        m3gRaiseError(m3g, M3G_INVALID_OPERATION);
  1.1565 +        return;
  1.1566 +    }
  1.1567 +    if (srcPalette == NULL) {
  1.1568 +        m3gRaiseError(m3g, M3G_NULL_POINTER);
  1.1569 +        return;
  1.1570 +    }
  1.1571 +    if (!m3gInRange(paletteLength, 0, 256)) {
  1.1572 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1573 +        return;
  1.1574 +    }
  1.1575 +
  1.1576 +    /*
  1.1577 +     * Copy the palette data into the allocated palette (for natively
  1.1578 +     * supported paletted formats), or remap the existing image data
  1.1579 +     * using the supplied palette entries (for non-native formats)
  1.1580 +     *
  1.1581 +     * NOTE the latter is a one-time operation!
  1.1582 +     */
  1.1583 +    if (img->paletteBytes > 0) {
  1.1584 +        M3Gubyte *palette = (M3Gubyte *)m3gMapObject(m3g, img->data);
  1.1585 +#       if defined(M3G_NGL_TEXTURE_API)
  1.1586 +        m3gConvertPixels(m3gPixelFormat(img->format), srcPalette,
  1.1587 +                         M3G_RGBA8, palette,
  1.1588 +                         paletteLength);
  1.1589 +#       else
  1.1590 +        M3Gsizei bpp = m3gBytesPerPixel(m3gPixelFormat(img->format));
  1.1591 +        m3gCopy(palette, srcPalette, (size_t) paletteLength * bpp);
  1.1592 +#       endif
  1.1593 +    }
  1.1594 +    else {
  1.1595 +        M3Gint count = img->width * img->height;
  1.1596 +        M3Gubyte *pixel = (M3Gubyte*)m3gMapObject(m3g, img->data);
  1.1597 +        const M3Gubyte *bytePalette = (const M3Gubyte *) srcPalette;
  1.1598 +
  1.1599 +        /* We need to treat the input and internal formats as
  1.1600 +         * separate, as the internal storage may be padded to more
  1.1601 +         * bytes than there are color components */
  1.1602 +
  1.1603 +        M3GPixelFormat paletteFormat = m3gPixelFormat(img->format);
  1.1604 +        const int numComponents = m3gBytesPerPixel(paletteFormat);
  1.1605 +        M3GPixelFormat imgFormat = img->internalFormat;
  1.1606 +        const int imgBpp = m3gBytesPerPixel(imgFormat);
  1.1607 +
  1.1608 +        /* In most cases we can just copy the corresponding palette
  1.1609 +         * entry on top of each pixel based on the pixel intensity (R
  1.1610 +         * or L component), but special formats require a more
  1.1611 +         * complicated conversion.  We just use the (slow) general
  1.1612 +         * conversion routine, as it already incorporates support for
  1.1613 +         * all formats. */
  1.1614 +        
  1.1615 +        if (imgBpp >= numComponents) {
  1.1616 +            while (count--) {
  1.1617 +                int offset = (*pixel) * numComponents;
  1.1618 +                int c;
  1.1619 +                for (c = 0; c < numComponents; ++c) {
  1.1620 +                    *pixel++ = bytePalette[offset + c];
  1.1621 +                }
  1.1622 +                while (c++ < imgBpp) { /* padding for e.g. 24-bit RGB */
  1.1623 +                    *pixel++ = 0xFF;
  1.1624 +                }
  1.1625 +            }
  1.1626 +        }
  1.1627 +        else {
  1.1628 +            while (count--) {
  1.1629 +                int offset = (*pixel) * numComponents;
  1.1630 +                m3gConvertPixels(paletteFormat, &bytePalette[offset],
  1.1631 +                                 imgFormat, pixel,
  1.1632 +                                 1);
  1.1633 +                pixel += imgBpp;
  1.1634 +            }
  1.1635 +        }
  1.1636 +    }
  1.1637 +    m3gUnmapObject(m3g, img->data);
  1.1638 +    m3gInvalidateImage(img);
  1.1639 +}
  1.1640 +
  1.1641 +/*!
  1.1642 + * \brief Sets a scanline of an image
  1.1643 + * 
  1.1644 + * \param hImage Image object
  1.1645 + * \param line scanline
  1.1646 + * \param trueAlpha M3G_TRUE if the source image has an alpha channel,
  1.1647 + *                  M3G_FALSE if it should come from the RGB values;
  1.1648 + *                  this only matters for alpha-only destination images
  1.1649 + * \param pixels souce pixels
  1.1650 + */
  1.1651 +M3G_API void m3gSetImageScanline(M3GImage hImage,
  1.1652 +                                 M3Gint line,
  1.1653 +                                 M3Gbool trueAlpha,
  1.1654 +                                 const M3Guint *pixels)
  1.1655 +{
  1.1656 +    Image *img = (Image *) hImage;
  1.1657 +    M3G_VALIDATE_OBJECT(img);
  1.1658 +
  1.1659 +    if (img->data == 0 || (img->flags & M3G_STATIC) != 0
  1.1660 +            || img->paletteBytes != 0) {
  1.1661 +        m3gRaiseError(M3G_INTERFACE(img), M3G_INVALID_OPERATION);
  1.1662 +        return;
  1.1663 +    }
  1.1664 +    
  1.1665 +    {
  1.1666 +        Interface *m3g = M3G_INTERFACE(img);
  1.1667 +        M3Gint stride = img->width * m3gBytesPerPixel(img->internalFormat);
  1.1668 +        M3Gubyte *dst = ((M3Gubyte *) m3gMapObject(m3g, img->data))
  1.1669 +            + img->paletteBytes;
  1.1670 +
  1.1671 +#ifdef M3G_NGL_TEXTURE_API
  1.1672 +        /* For RGB images without alpha channel, source alpha is
  1.1673 +         * forced to 0xff. */
  1.1674 +
  1.1675 +        if (img->format == M3G_RGB) {
  1.1676 +            M3Gint i;
  1.1677 +            M3Guint argb, *dst;
  1.1678 +
  1.1679 +            dst = (M3Guint *) pixels;
  1.1680 +
  1.1681 +            for (i = 0; i < img->width; i++) {
  1.1682 +                argb = *dst | 0xff000000;
  1.1683 +                *dst++ = argb;
  1.1684 +            }
  1.1685 +        }
  1.1686 +#endif
  1.1687 +
  1.1688 +        /* Note that an alpha-only destination format is faked for
  1.1689 +         * luminance if the source contained no true alpha data; alpha
  1.1690 +         * is then inferred from the RGB values instead */
  1.1691 +        
  1.1692 +        convertFromARGB(pixels,
  1.1693 +                        img->width,
  1.1694 +                        (img->internalFormat == M3G_A8 && !trueAlpha) ? M3G_L8 : img->internalFormat,
  1.1695 +                        dst + line * stride);
  1.1696 +
  1.1697 +        m3gUnmapObject(m3g, img->data);
  1.1698 +        m3gInvalidateImage(img);
  1.1699 +    }
  1.1700 +}
  1.1701 +
  1.1702 +/*!
  1.1703 + * \brief Sets a rectangular subregion of an image
  1.1704 + * 
  1.1705 + * \param hImage Image object
  1.1706 + * \param x x-coordinate in destination image
  1.1707 + * \param y y-coordinate in destination image
  1.1708 + * \param width width of source pixels
  1.1709 + * \param height height of source pixels
  1.1710 + * \param length length of source data, in bytes
  1.1711 + * \param pixels source pixels
  1.1712 + */
  1.1713 +M3G_API void m3gSetSubImage(M3GImage hImage,
  1.1714 +                            M3Gint x, M3Gint y,
  1.1715 +                            M3Gint width, M3Gint height,
  1.1716 +                            M3Gint length, const void *pixels)
  1.1717 +{
  1.1718 +    Interface *m3g;
  1.1719 +    Image *img = (Image *) hImage;
  1.1720 +
  1.1721 +    M3GPixelFormat srcFormat;
  1.1722 +    M3Gsizei srcBpp;
  1.1723 +
  1.1724 +    M3G_VALIDATE_OBJECT(img);
  1.1725 +    m3g = M3G_INTERFACE(img);
  1.1726 +
  1.1727 +    /* Check for errors */
  1.1728 +
  1.1729 +    if (img->data == 0 || (img->flags & M3G_STATIC) != 0) {
  1.1730 +        M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
  1.1731 +        m3gRaiseError(m3g, M3G_INVALID_OPERATION);
  1.1732 +        return;
  1.1733 +    }
  1.1734 +    if (pixels == NULL) {
  1.1735 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1736 +        return;
  1.1737 +    }
  1.1738 +    if (x < 0 || y < 0 || width <= 0 || height <= 0
  1.1739 +            || x+width > img->width || y+height > img->height) {
  1.1740 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1741 +        return;
  1.1742 +    }
  1.1743 +    
  1.1744 +    srcFormat = m3gInputDataFormat(img);
  1.1745 +    srcBpp = m3gBytesPerPixel(srcFormat);
  1.1746 +    
  1.1747 +    if (length < width * height * srcBpp) {
  1.1748 +        m3gRaiseError(m3g, M3G_INVALID_VALUE);
  1.1749 +        return;
  1.1750 +    }    
  1.1751 +
  1.1752 +    /* Copy the image data, doing a conversion if the input format
  1.1753 +     * does not match the internal storage format */
  1.1754 +    {
  1.1755 +        const M3Gubyte *srcPixels = (const M3Gubyte*) pixels;
  1.1756 +        M3Gsizei srcStride = width * srcBpp;
  1.1757 +
  1.1758 +        M3GPixelFormat dstFormat = img->internalFormat;
  1.1759 +        M3Gsizei dstBpp = m3gBytesPerPixel(dstFormat);
  1.1760 +        M3Gsizei dstStride = img->width * dstBpp;
  1.1761 +        M3Gubyte *dstPixels =
  1.1762 +            ((M3Gubyte *)m3gMapObject(m3g, img->data))
  1.1763 +            + img->paletteBytes
  1.1764 +            + y * dstStride + x * dstBpp;
  1.1765 +        
  1.1766 +        M3Gint numLines = height, numPixels = width;
  1.1767 +        M3Gbool paletted = (img->flags & M3G_PALETTED) != 0;
  1.1768 +        
  1.1769 +        /* Optimize the copy for full image width */
  1.1770 +        
  1.1771 +        if (width == img->width) {
  1.1772 +            numLines = 1;
  1.1773 +            numPixels = width * height;
  1.1774 +        }
  1.1775 +        
  1.1776 +        /* Copy a scanline at a time, converting as necessary */
  1.1777 +        
  1.1778 +        while (numLines-- > 0) {
  1.1779 +            
  1.1780 +            /* Matching pixel formats are just copied without
  1.1781 +             * conversion, and all internally supported paletted
  1.1782 +             * formats match each other physically, so they can be
  1.1783 +             * copied as well */
  1.1784 +        
  1.1785 +            if (dstFormat == srcFormat || img->paletteBytes > 0) {
  1.1786 +                m3gCopy(dstPixels, srcPixels, numPixels * dstBpp);
  1.1787 +            }
  1.1788 +            else {
  1.1789 +                if (!paletted) {
  1.1790 +
  1.1791 +                    /* Ordinary conversion into an internal format
  1.1792 +                     * that is encoded differently from the external
  1.1793 +                     * format; can not be a paletted image */
  1.1794 +
  1.1795 +                    M3G_ASSERT((img->flags & M3G_PALETTED) == 0);
  1.1796 +                    m3gConvertPixels(srcFormat, srcPixels,
  1.1797 +                                     dstFormat, dstPixels,
  1.1798 +                                     numPixels);
  1.1799 +                }
  1.1800 +                else {
  1.1801 +                    M3G_ASSERT(!m3gSupportedPaletteFormat(img->format));
  1.1802 +                    
  1.1803 +                    /* Palette indices for one-byte-per-pixel formats
  1.1804 +                     * are just copied in and mapped to actual values
  1.1805 +                     * later; multibyte paletted formats require a
  1.1806 +                     * conversion into LA, RGB, or RGBA format
  1.1807 +                     * intensity levels temporarily before remapping
  1.1808 +                     * to actual colors in m3gSetImagePalette */
  1.1809 +                    
  1.1810 +                    if (dstBpp == 1) {
  1.1811 +                        m3gCopy(dstPixels, srcPixels, numPixels);
  1.1812 +                    }
  1.1813 +                    else {
  1.1814 +                        m3gConvertPixels(M3G_L8, srcPixels,
  1.1815 +                                         dstFormat, dstPixels,
  1.1816 +                                         numPixels);
  1.1817 +                    }
  1.1818 +                }
  1.1819 +            }
  1.1820 +            
  1.1821 +            srcPixels += srcStride;
  1.1822 +            dstPixels += dstStride;
  1.1823 +        }
  1.1824 +
  1.1825 +        /* Release the image data and invalidate mipmap levels */
  1.1826 +        
  1.1827 +        m3gUnmapObject(m3g, img->data);
  1.1828 +        m3gInvalidateImage(img);
  1.1829 +    }
  1.1830 +    M3G_VALIDATE_OBJECT(img);
  1.1831 +}
  1.1832 +
  1.1833 +#undef SPAN_BUFFER_SIZE
  1.1834 +