First public contribution.
2 * Copyright (c) 2003 Nokia Corporation and/or its subsidiary(-ies).
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".
9 * Initial Contributors:
10 * Nokia Corporation - initial contribution.
14 * Description: Image implementation
22 * \brief Image implementation
26 #ifndef M3G_CORE_INCLUDE
27 # error included by m3g_core.c; do not compile separately.
30 #include "m3g_image.h"
31 #include "m3g_texture.h"
33 /* Declare prototypes for some of the helper functions called from the
34 * platform-dependent code included below */
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);
41 /* Include platform-dependent functionality */
42 #include "m3g_image.inl"
44 /* Size of the buffer used in pixel format conversions; this should be
46 #define SPAN_BUFFER_SIZE 32
48 M3G_CT_ASSERT((SPAN_BUFFER_SIZE & 1) == 0);
50 /*----------------------------------------------------------------------
52 *--------------------------------------------------------------------*/
56 * \brief Destroys this Image object.
58 * \param obj Image object
60 static void m3gDestroyImage(Object *obj)
62 Image *image = (Image*)obj;
63 Interface *m3g = M3G_INTERFACE(image);
64 M3G_VALIDATE_OBJECT(image);
67 m3gFreeObject(m3g, image->data);
68 m3gFreeObject(m3g, image->mipData);
70 M3G_ASSIGN_REF(image->copyOf, NULL);
72 if (image->powerOfTwo != image) {
73 M3G_ASSIGN_REF(image->powerOfTwo, NULL);
76 # if !defined(M3G_NGL_TEXTURE_API)
77 if (image->texObject) {
78 m3gDeleteGLTextures(m3g, 1, &image->texObject);
80 if (image->large != NULL) {
81 m3gDestroyLargeImage(image);
85 m3gDestroyObject(obj);
88 /*--------------------------------------------------------------------*/
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)
95 #define RGBSUM(argb) (0x4CB2u * RED(argb) + \
96 0x9691u * GREEN(argb) + \
100 * \internal \brief ARGB -> A
102 static void convertARGBToA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
105 *dst++ = (M3Gubyte) ALPHA(*src++);
110 * \internal \brief ARGB -> L
112 static void convertARGBToL8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
115 M3Guint argb = *src++;
116 M3Guint sum = RGBSUM(argb);
117 *dst++ = (M3Gubyte)(sum >> 16);
122 * \internal \brief ARGB -> LA */
123 static void convertARGBToLA4(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
126 M3Guint argb = *src++;
127 M3Guint sum = RGBSUM(argb);
128 *dst++ = (M3Gubyte)(((sum >> 16) & 0xF0) | ((argb >> 28) & 0x0F));
133 * \internal \brief ARGB -> LA8
135 static void convertARGBToLA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
138 M3Guint argb = *src++;
139 M3Guint sum = RGBSUM(argb);
140 *dst++ = (M3Gubyte)(sum >> 16); /* L */
141 *dst++ = (M3Gubyte) ALPHA(argb);
146 * \internal \brief ARGB -> RGB
148 static void convertARGBToRGB8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
151 M3Guint argb = *src++;
152 *dst++ = (M3Gubyte) RED(argb);
153 *dst++ = (M3Gubyte) GREEN(argb);
154 *dst++ = (M3Gubyte) BLUE(argb);
159 * \internal \brief ARGB -> RGB565
161 static void convertARGBToRGB565(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
164 M3Guint argb = *src++;
165 *(M3Gushort*)dst = (M3Gushort)(((argb >> 8) & 0xF800u)|
166 ((argb >> 5) & 0x07E0u)|
167 ((argb >> 3) & 0x001Fu));
173 * \internal \brief ARGB -> RGBA
175 static void convertARGBToRGBA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
178 M3Guint argb = *src++;
179 *dst++ = (M3Gubyte) RED(argb);
180 *dst++ = (M3Gubyte) GREEN(argb);
181 *dst++ = (M3Gubyte) BLUE(argb);
182 *dst++ = (M3Gubyte) ALPHA(argb);
187 * \internal \brief ARGB -> BGRA
189 static void convertARGBToBGRA8(const M3Guint *src, M3Gsizei count, M3Gubyte *dst)
192 M3Guint argb = *src++;
193 *dst++ = (M3Gubyte) BLUE(argb);
194 *dst++ = (M3Gubyte) GREEN(argb);
195 *dst++ = (M3Gubyte) RED(argb);
196 *dst++ = (M3Gubyte) ALPHA(argb);
207 * \brief Converts a span of ARGB pixels to another format
209 * \param src source pixels
210 * \param count pixel count
211 * \param dstFormat destination format
212 * \param dst destination pixels
214 static void convertFromARGB(const M3Guint *src,
216 M3GPixelFormat dstFormat,
221 convertARGBToL8(src, count, dst);
224 convertARGBToA8(src, count, dst);
227 convertARGBToLA4(src, count, dst);
230 convertARGBToLA8(src, count, dst);
233 convertARGBToRGB8(src, count, dst);
236 convertARGBToRGB565(src, count, dst);
240 convertARGBToRGBA8(src, count, dst);
244 convertARGBToBGRA8(src, count, dst);
247 M3G_ASSERT(M3G_FALSE); /* conversion not supported */
251 /*--------------------------------------------------------------------*/
254 * \internal \brief A8 -> ARGB
256 static void convertA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
259 M3Guint argb = M3G_RGB_MASK;
260 argb |= ((M3Guint) *src++) << 24;
266 * \internal \brief L8 -> ARGB
268 static void convertL8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
271 M3Guint argb = *src++;
272 argb |= (argb << 8) | (argb << 16);
273 argb |= M3G_ALPHA_MASK;
279 * \internal \brief LA8 -> ARGB
281 static void convertLA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
284 M3Guint argb = *src++;
285 argb |= (argb << 8) | (argb << 16);
286 argb |= ((M3Guint) *src++) << 24;
292 * \internal \brief RGB8 -> ARGB
294 static void convertRGB8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
297 M3Guint argb = M3G_ALPHA_MASK;
298 argb |= ((M3Guint)(*src++)) << 16;
299 argb |= ((M3Guint)(*src++)) << 8;
300 argb |= (M3Guint)(*src++);
306 * \internal \brief RGB565 -> ARGB
308 static void convertRGB565ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
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);
322 * \internal \brief RGBA8 -> ARGB
324 static void convertRGBA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
328 argb = ((M3Guint)(*src++)) << 16;
329 argb |= ((M3Guint)(*src++)) << 8;
330 argb |= (M3Guint)(*src++);
331 argb |= ((M3Guint)(*src++)) << 24;
337 * \internal \brief BGRA8 -> ARGB
339 static void convertBGRA8ToARGB(const M3Gubyte *src, M3Gsizei count, M3Guint *dst)
343 argb = (M3Guint)(*src++);
344 argb |= ((M3Guint)(*src++)) << 8;
345 argb |= ((M3Guint)(*src++)) << 16;
346 argb |= ((M3Guint)(*src++)) << 24;
353 * \brief Converts a span of pixels to ARGB
355 * \param srcFormat source format
356 * \param src source pixels
357 * \param count pixel count
358 * \param dst destination pixels
360 static void convertToARGB(M3GPixelFormat srcFormat,
367 convertA8ToARGB(src, count, dst);
370 convertL8ToARGB(src, count, dst);
373 convertLA8ToARGB(src, count, dst);
376 convertRGB8ToARGB(src, count, dst);
379 convertRGB565ToARGB(src, count, dst);
383 convertRGBA8ToARGB(src, count, dst);
387 convertBGRA8ToARGB(src, count, dst);
390 M3G_ASSERT(M3G_FALSE); /* conversion not supported */
396 * \brief Fast path for BGRA-to-RGBA conversion
398 #if defined (M3G_HW_ARMV6)
399 __asm void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
400 M3Gsizei width, M3Gsizei height,
411 CMP r3, #0 // if height = 0, do nothing
414 STMFD sp!, {r4-r12, lr}
416 LDR r12, [sp, #(10*4)]
417 SUB r1, r1, r2, LSL #2
420 _fastConvertBGRAToRGBA_outerLoop
421 MOVS r2, r14, ASR #2 // amount of 4x32 bit writes
422 BEQ _fastConvertBGRAToRGBA_tail
424 _fastConvertBGRAToRGBA_innerLoop
426 LDMIA r0!, {r4-r7} // AARRGGBB
428 REV r4, r4 // BBGGRRAA
432 MOV r8, r4, ROR #8 // AABBGGRR
437 BNE _fastConvertBGRAToRGBA_innerLoop
439 _fastConvertBGRAToRGBA_tail
441 SUBS r2, r14, r2, LSL #2 // number of remaining writes in the tail
443 _fastConvertBGRAToRGBA_tail_loop
450 BNE _fastConvertBGRAToRGBA_tail_loop
454 BNE _fastConvertBGRAToRGBA_outerLoop
456 LDMFD sp!, {r4-r12, lr}
460 #else /* #if defined (M3G_HW_ARMV6) */
461 static void fastConvertBGRAToRGBA(const M3Gubyte *src, M3Gsizei srcStride,
462 M3Gsizei width, M3Gsizei height,
465 unsigned int pixel, pixel2;
467 unsigned int mask = 0x00ff00ff;
468 int spanwidth = (width >> 1) - 1;
470 unsigned int *d = (unsigned int *)dst;
472 M3G_ASSERT(width > 2);
474 for (y = 0; y < height; ++y) {
475 unsigned int *s = (unsigned int *)(src + y*srcStride);
479 for (x = 0; x < spanwidth; ++x) {
482 temp = pixel & mask; /* 00RR00BB */
483 pixel = pixel - temp; /* AA00GG00 */
484 pixel = pixel | (temp << 16); /* AABBGG00 */
485 *d++ = pixel | (temp >> 16); /* AABBGGRR */
489 temp = pixel2 & mask; /* 00RR00BB */
490 pixel2 = pixel2 - temp; /* AA00GG00 */
491 pixel2 = pixel2 | (temp << 16); /* AABBGG00 */
492 *d++ = pixel2 | (temp >> 16); /* AABBGGRR */
496 temp = pixel & mask; /* 00RR00BB */
497 pixel = pixel - temp; /* AA00GG00 */
498 pixel = pixel | (temp << 16); /* AABBGG00 */
499 *d++ = pixel | (temp >> 16); /* AABBGGRR */
501 temp = pixel2 & mask; /* 00RR00BB */
502 pixel2 = pixel2 - temp; /* AA00GG00 */
503 pixel2 = pixel2 | (temp << 16); /* AABBGG00 */
504 *d++ = pixel2 | (temp >> 16); /* AABBGGRR */
507 #endif /* #if defined (M3G_HW_ARMV6) */
509 /*--------------------------------------------------------------------*/
513 * \brief Maps a logical image format to an internal pixel format
515 * \param imgFormat logical image format
516 * \param paletted paletted flag
517 * \return the internal image pixel format
519 static M3GPixelFormat getInternalFormat(M3GImageFormat imgFormat,
525 # if defined(M3G_NGL_TEXTURE_API)
526 return M3G_PALETTE8_RGB8_32;
528 return M3G_PALETTE8_RGB8;
531 return M3G_PALETTE8_RGBA8;
533 M3G_ASSERT(M3G_FALSE);
534 return (M3GPixelFormat)0;
538 M3GPixelFormat format = m3gPixelFormat(imgFormat);
540 # if defined(M3G_NGL_TEXTURE_API)
541 if (format == M3G_RGB8) {
542 return (M3G_USE_16BIT_TEXTURES) ? M3G_RGB565 : M3G_RGB8_32;
544 if (format == M3G_LA8) {
555 * \brief Gets the correct pixel format for setting data to an image
557 static M3GPixelFormat m3gInputDataFormat(const Image *img)
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 */
562 if (img->flags & M3G_PALETTED) {
563 return M3G_PALETTE8_RGBA8;
566 return m3gPixelFormat(img->format);
572 * \brief Returns log2(resolution)+1. Assumes that resolution is power of two.
574 * \param w width in pixels
575 * \param h height in pixels
576 * \return number of needed mipmap levels
578 static M3Gint m3gGetNumMipmapLevels(M3Gint w, M3Gint h)
580 M3Gint res = (w > h) ? w : h;
591 * \brief Downsamples an image to half the original size
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
600 static void m3gDownsample(M3GPixelFormat format,
601 const M3Gubyte *srcPixels,
602 M3Gint *pw, M3Gint *ph,
605 M3Gint i, j, bpp, pixStride, lineStride;
606 M3Gint w = *pw, h = *ph;
608 M3Guint temp[2][SPAN_BUFFER_SIZE/2];
610 M3G_ASSERT_PTR(srcPixels);
611 M3G_ASSERT(w >= 1 && h >= 1);
613 bpp = m3gBytesPerPixel(format);
614 lineStride = (h > 1) ? w * bpp : 0;
615 pixStride = (w > 1) ? bpp : 0;
619 /* Iterate over buffer-sized blocks in the image */
621 for (j = 0; j < h; j += 2) {
622 for (i = 0; i < w; i += SPAN_BUFFER_SIZE/2) {
624 /* Fill the buffer from the source image */
626 const M3Gubyte *src = srcPixels + (j*lineStride + i*pixStride);
627 M3Gint c = SPAN_BUFFER_SIZE/2;
631 convertToARGB(format, src, c, &temp[0][0]);
632 convertToARGB(format, src + lineStride, c, &temp[1][0]);
634 temp[0][1] = temp[0][0];
635 temp[1][1] = temp[1][0];
638 /* Average the pixels in the buffer */
640 # define AG_MASK 0xFF00FF00u
641 # define RB_MASK 0x00FF00FFu
644 for (k = 0; k < c; k += 2) {
647 /* Add two components in parallel */
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);
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);
659 /* Shift to divide by 4, adding ½ for rounding */
661 temp[0][k>>1] = ((((ag + 0x00020002u) << 6) & AG_MASK) |
662 (((rb + 0x00020002u) >> 2) & RB_MASK));
669 /* Write result to the output buffer */
671 convertFromARGB(&temp[0][0], c>>1, format, dst);
676 /* Return output width and height */
688 * \brief Returns the OpenGL minification filter corresponding to M3G
691 static GLenum m3gGetGLMinFilter(M3Genum levelFilter, M3Genum imageFilter)
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
699 return minFilter[levelFilter - M3G_FILTER_BASE_LEVEL][imageFilter - M3G_FILTER_LINEAR];
702 /*----------------------------------------------------------------------
704 *--------------------------------------------------------------------*/
708 * \brief Converts an internal ARGB color to four GLfixed components
710 static void m3gGLColor(M3Guint argb, GLfixed *dst)
714 r = (GLfixed)((argb & 0x00FF0000u) >> 16);
715 g = (GLfixed)((argb & 0x0000FF00u) >> 8);
716 b = (GLfixed)( argb & 0x000000FFu );
717 a = (GLfixed)((argb & 0xFF000000u) >> 24);
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);
727 * \brief Binds an image into the current texture unit and sets up
730 static void m3gBindTextureImage(Image *img, M3Genum levelFilter, M3Genum imageFilter)
734 /* We have no mipmap generation for paletted images, so disable
735 * mipmapping in that case */
737 if (m3gIsInternallyPaletted(img)) {
738 levelFilter = M3G_FILTER_BASE_LEVEL;
741 /* Bind the OpenGL texture object, generating mipmaps if
744 m3gBindTextureObject(img, levelFilter != M3G_FILTER_BASE_LEVEL);
746 /* Set up OpenGL texture filtering according to our flags */
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));
758 * \brief Maps a logical image format to the matching default pixel
761 * \param imgFormat logical image format
762 * \return a one-byte-per-pixel pixel format
764 static M3GPixelFormat m3gPixelFormat(M3GImageFormat imgFormat)
771 case M3G_LUMINANCE_ALPHA:
778 M3G_ASSERT(M3G_FALSE);
779 return M3G_NO_FORMAT;
785 * \brief Returns the number of bytes per pixel in a given pixel format
787 * \param format pixel format
788 * \return bytes per pixel
790 static M3Gint m3gBytesPerPixel(M3GPixelFormat format)
796 case M3G_PALETTE8_RGB8:
797 case M3G_PALETTE8_RGB8_32:
798 case M3G_PALETTE8_RGBA8:
815 M3G_ASSERT(M3G_FALSE);
822 * \brief Converts pixels between formats
824 * \note Only a limited subset of source and destination formats may
825 * be supported; see the \c convert functions in m3g_image.c
827 * \param srcFormat source format
828 * \param src source pixels
829 * \param dstFormat destination format
830 * \param dst destination pixels
831 * \param count pixel count
833 static void m3gConvertPixels(M3GPixelFormat srcFormat, const M3Gubyte *src,
834 M3GPixelFormat dstFormat, M3Gubyte *dst,
837 M3Guint temp[SPAN_BUFFER_SIZE];
838 const char endianTest[4] = { 1, 0, 0, 0 };
840 M3Guint srcBpp = m3gBytesPerPixel(srcFormat);
841 M3Guint dstBpp = m3gBytesPerPixel(dstFormat);
842 M3G_ASSERT(srcBpp > 0 && dstBpp > 0);
847 /* Check the source and destination formats to avoid
848 the intermediate ARGB format conversion. */
849 if (((srcFormat == M3G_RGBA8 && (dstFormat == M3G_BGRA8 || dstFormat == M3G_BGR8_32))
850 || (dstFormat == M3G_RGBA8 && (srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32)))
851 && (n > 2) && ((*(const int *)endianTest) == 1)) {
852 /* use fast path for RGBA<->BGRA conversion */
853 fastConvertBGRAToRGBA(src, n * srcBpp, n, 1, dst);
854 } else if (srcFormat == M3G_ARGB8 && dstFormat != M3G_ARGB8) {
855 convertFromARGB((M3Guint*)src, n, dstFormat, dst);
856 } else if (srcFormat != M3G_ARGB8 && dstFormat == M3G_ARGB8) {
857 convertToARGB(srcFormat, src, n, (M3Guint*)dst);
859 /* no luck, do the conversion via ARGB (source format -> ARGB -> destination format) */
860 n = (count < SPAN_BUFFER_SIZE) ? count : SPAN_BUFFER_SIZE;
861 convertToARGB(srcFormat, src, n, temp);
862 convertFromARGB(temp, n, dstFormat, dst);
872 * \brief Copies image data. The source image is copied to
873 * the destination image.
875 * \param dst destination image
876 * \param src source image
878 static void m3gCopyImagePixels(Image *dst,
881 const M3Gubyte *pSrc;
885 /* Check inputs (debug only!) */
886 M3G_VALIDATE_OBJECT(dst);
887 M3G_VALIDATE_OBJECT(src);
889 M3G_ASSERT(src->internalFormat == dst->internalFormat);
890 M3G_ASSERT(src->format == dst->format);
892 M3G_ASSERT(src->paletteBytes == dst->paletteBytes);
894 /* Compute source and destination pixel data pointers */
895 pSrc = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(src), src->data);
896 pDst = (M3Gubyte *)m3gMapObject(M3G_INTERFACE(dst), dst->data);
898 bpp = m3gBytesPerPixel(src->internalFormat);
900 if (src->paletteBytes > 0) {
901 m3gCopy(pDst, pSrc, src->paletteBytes);
902 pDst += dst->paletteBytes;
903 pSrc += src->paletteBytes;
906 /* Do a straight copy if the sizes match, or resample if not */
907 if (src->width == dst->width && src->height == dst->height ) {
908 m3gCopy(pDst, pSrc, src->width * src->height * bpp);
911 /* Adder values as 8.8 fixed point */
915 xAdd = (256 * src->width) / dst->width;
916 yAdd = (256 * src->height) / dst->height;
918 for (y = 0; y < dst->height; y++) {
919 for (x = 0; x < dst->width; x++) {
920 m3gCopy(pDst, pSrc + bpp * (((xAdd * x) >> 8) + ((yAdd * y) >> 8) * src->width), bpp);
926 m3gUnmapObject(M3G_INTERFACE(dst), dst->data);
927 m3gUnmapObject(M3G_INTERFACE(src), src->data);
929 m3gInvalidateImage(dst);
934 * \brief Invalidates any cached data for this image
936 * Used when rendering to the image.
938 * \param img Image object
940 static void m3gInvalidateImage(Image *img)
942 M3G_VALIDATE_OBJECT(img);
943 img->dirty = M3G_TRUE;
945 # if !defined(M3G_NGL_TEXTURE_API)
947 img->large->dirty = M3G_TRUE;
949 # endif /*M3G_NGL_TEXTURE_API*/
951 if (img->powerOfTwo != img) {
952 img->powerOfTwoDirty = M3G_TRUE;
958 * \brief Overloaded Object3D method.
960 * \param originalObj original Image object
961 * \param cloneObj pointer to cloned Image object
962 * \param pairs array for all object-duplicate pairs
963 * \param numPairs number of pairs
965 static M3Gbool m3gImageDuplicate(const Object *originalObj,
970 Image *original = (Image *)originalObj;
973 /* If the original image still has its pixel data, make a full
974 * copy -- this is wasteful for immutable images, but the shame's
975 * on the user in that case */
977 if (original->data) {
978 clone = (Image*) m3gCreateImage(originalObj->interface,
986 /* Otherwise, just point to the original and use its data
989 clone = (Image*) m3gAlloc(M3G_INTERFACE(original), sizeof(*clone));
991 M3G_ASSIGN_REF(clone->copyOf, original);
994 *cloneObj = (Object *)clone;
995 if (*cloneObj == NULL) {
999 if (m3gObjectDuplicate(originalObj, cloneObj, pairs, numPairs)) {
1000 /* Copy image contents */
1001 if (original->data) {
1002 m3gCopyImagePixels(clone, original);
1014 * \brief Frees the pixel data associated with this image; used for
1015 * optimizing memory usage after copying the data to a secondary
1018 static void m3gFreeImageData(Image *img)
1020 M3G_ASSERT(img->format == M3G_RGB || img->format == M3G_LUMINANCE);
1021 # if !defined(M3G_NGL_TEXTURE_API)
1022 M3G_ASSERT(!img->mipmapsDirty);
1024 M3G_ASSERT(!img->powerOfTwoDirty);
1025 M3G_ASSERT(img->powerOfTwo != NULL);
1026 M3G_ASSERT(!img->pinned);
1028 M3G_LOG1(M3G_LOG_IMAGES, "Freeing copy of image 0x%08X\n",
1032 m3gFreeObject(M3G_INTERFACE(img), img->data);
1034 m3gFreeObject(M3G_INTERFACE(img), img->mipData);
1037 M3G_ASSIGN_REF(img->copyOf, NULL);
1042 * \brief Returns a power-of-two variant of an image
1044 * This is used for sprites and background images.
1046 static Image *m3gGetPowerOfTwoImage(Image *img)
1048 M3G_VALIDATE_OBJECT(img);
1050 /* Create a power-of-two variant of the image if one doesn't exist
1053 if (img->powerOfTwo == NULL) {
1055 M3Gint width, height;
1059 M3G_ASSERT(!m3gIsPowerOfTwo(img->width) ||
1060 !m3gIsPowerOfTwo(img->height));
1062 /* Choose new image size to allow a maximum shrinkage of 25%;
1063 * this is to weed out pathological cases of quadruple memory
1064 * usage because an image is one pixel too wide */
1066 width = m3gNextPowerOfTwo((img->width * 3) >> 2);
1067 height = m3gNextPowerOfTwo((img->height * 3) >> 2);
1069 width = M3G_MIN(width, M3G_MAX_TEXTURE_DIMENSION);
1070 height = M3G_MIN(height, M3G_MAX_TEXTURE_DIMENSION);
1072 flags = img->flags & (~M3G_RENDERING_TARGET);
1074 potImage = m3gCreateImage(M3G_INTERFACE(img),
1079 return NULL; /* automatic out-of-memory */
1082 M3G_ASSIGN_REF(img->powerOfTwo, potImage);
1083 img->powerOfTwoDirty = M3G_TRUE;
1086 /* Update POT image data if necessary */
1088 if (img->powerOfTwoDirty) {
1089 m3gCopyImagePixels(img->powerOfTwo, img);
1090 img->powerOfTwoDirty = M3G_FALSE;
1092 /* Get rid of the original at this point if we can */
1095 m3gFreeImageData(img);
1099 return img->powerOfTwo;
1104 * \brief Gets image alpha at x, y.
1106 * \param image Image object
1107 * \param x x-coordinate
1108 * \param y y-coordinate
1109 * \return alpha value
1112 static M3Gint m3gGetAlpha(Image *image, M3Gint x, M3Gint y)
1115 M3Gint bpp = m3gBytesPerPixel(image->internalFormat);
1119 /* Quick exit for non-alpha formats */
1121 if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
1125 /* For other formats, we have to sample the image data */
1128 Image *potImage = image->powerOfTwo;
1129 M3G_ASSERT(potImage != image);
1130 return m3gGetAlpha(potImage,
1131 (x * image->width) / potImage->width,
1132 (y * image->height) / potImage->height);
1135 pixels = ((M3Gubyte *)m3gMapObject(M3G_INTERFACE(image), image->data));
1137 if (image->paletteBytes == 0) {
1139 data = pixels[x + y * image->width];
1141 else if (bpp == 2) {
1142 data = ((M3Gushort *)pixels)[x + y * image->width];
1145 data = ((M3Guint *)pixels)[x + y * image->width];
1150 palette = (M3Guint *)pixels;
1151 pixels += image->paletteBytes;
1153 data = palette[pixels[x + y * image->width]];
1156 m3gUnmapObject(M3G_INTERFACE(image), image->data);
1158 switch (image->internalFormat) {
1170 /* Should never be here!! */
1171 M3G_ASSERT(M3G_FALSE);
1179 * \brief Computes the scanline stride of an image
1181 static M3Gsizei m3gGetImageStride(const Image *img)
1183 M3G_VALIDATE_OBJECT(img);
1184 return img->width * m3gBytesPerPixel(img->internalFormat);
1187 /*----------------------------------------------------------------------
1188 * Virtual function table
1189 *--------------------------------------------------------------------*/
1191 static const ObjectVFTable m3gvf_Image = {
1192 m3gObjectApplyAnimation,
1193 m3gObjectIsCompatible,
1194 m3gObjectUpdateProperty,
1195 m3gObjectDoGetReferences,
1202 /*----------------------------------------------------------------------
1203 * Public API functions
1204 *--------------------------------------------------------------------*/
1207 * \brief Creates a new Image
1209 * \param interface M3G interface
1210 * \param srcFormat source format
1211 * \param width width in pixels
1212 * \param height height in pixels
1213 * \param flags creation flags; a combination of
1214 * M3G_DYNAMIC, M3G_STATIC,
1215 * M3G_RENDERING_TARGET, and M3G_PALETTED
1216 * \retval Image new Image object
1217 * \retval NULL Image creating failed
1219 M3G_API M3GImage m3gCreateImage(/*@dependent@*/ M3GInterface interface,
1220 M3GImageFormat srcFormat,
1221 M3Gint width, M3Gint height,
1224 Interface *m3g = (Interface *) interface;
1225 M3G_VALIDATE_INTERFACE(m3g);
1229 if (width <= 0 || height <= 0) {
1230 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1234 if (!m3gInRange(srcFormat, M3G_ALPHA, M3G_RGBA)) {
1235 m3gRaiseError(m3g, M3G_INVALID_ENUM);
1239 /* Parameters OK; allocate and initialize the object */
1242 Image *img = m3gAllocZ(m3g, sizeof(Image));
1247 /* Clean up and set flags */
1249 M3G_LOG3(M3G_LOG_IMAGES, "Image 0x%08X is %d x %d",
1250 (unsigned) img, width, height);
1252 flags |= M3G_DYNAMIC; /* the default */
1254 if (flags & M3G_STATIC) {
1255 M3G_LOG(M3G_LOG_IMAGES, ", immutable");
1256 flags &= ~M3G_DYNAMIC;
1258 if (flags & M3G_RENDERING_TARGET) {
1259 M3G_LOG(M3G_LOG_IMAGES, ", rendertarget");
1260 flags |= M3G_DYNAMIC;
1262 if (flags & M3G_PALETTED) {
1263 M3G_LOG(M3G_LOG_IMAGES, ", paletted");
1267 M3G_LOG(M3G_LOG_IMAGES, "\n");
1270 /* Allocate pixel & palette data; the palette is stored at
1271 * the beginning of the pixel data chunk */
1273 M3Gbool paletted = ((img->flags & M3G_PALETTED) != 0)
1274 && m3gSupportedPaletteFormat(srcFormat);
1275 M3GPixelFormat internalFormat = getInternalFormat(srcFormat,
1277 M3Guint bpp = m3gBytesPerPixel(internalFormat);
1278 M3Guint pixelBytes = width * height * bpp;
1280 if ((img->flags & M3G_PALETTED) != 0 && !paletted) {
1281 M3G_LOG(M3G_LOG_WARNINGS|M3G_LOG_IMAGES,
1282 "Warning: Unsupported paletted format\n");
1285 /* The palette will always have 256 elements and one byte
1286 * per color component (except padded 32-bit for NGL) */
1290 # if defined(M3G_NGL_TEXTURE_API)
1293 256 * m3gBytesPerPixel(m3gPixelFormat(srcFormat));
1297 /* Set up the rest of the image parameters */
1300 img->height = height;
1301 img->format = srcFormat;
1302 img->internalFormat = internalFormat;
1303 img->glFormat = m3gGetGLFormat(internalFormat);
1305 M3G_LOG1(M3G_LOG_IMAGES, "Image data %d bytes\n",
1306 pixelBytes + img->paletteBytes);
1308 /* Allocate the image memory */
1310 img->data = m3gAllocObject(m3g, pixelBytes + img->paletteBytes);
1311 if (img->data == 0) {
1316 #ifdef M3G_ENABLE_GLES_RESOURCE_HANDLING
1317 /* If GLES resource freeing (see function m3gFreeGLESResources)
1318 is enabled, the GL texture might get deleted at any point, so
1319 a copy of the texture data has to be always kept in memory. */
1320 img->pinned = M3G_TRUE;
1322 /* Lock the image data in memory if the image is dynamic,
1323 * or the format has alpha information; otherwise, we'll
1324 * be able to get rid of an extra copy when generating a
1325 * power-of-two version or uploading to OpenGL */
1327 if ((img->flags & M3G_DYNAMIC) != 0
1328 || (img->format != M3G_RGB &&
1329 img->format != M3G_LUMINANCE)) {
1330 img->pinned = M3G_TRUE;
1333 /* If the image can be used as a rendering target, clear
1334 * to opaque white by default */
1336 if ((img->flags & M3G_RENDERING_TARGET) != 0) {
1337 M3Gubyte *pixels = ((M3Gubyte *)m3gMapObject(m3g, img->data))
1338 + img->paletteBytes;
1339 m3gFill(pixels, (size_t) pixelBytes, -1);
1340 m3gUnmapObject(m3g, img->data);
1343 /* Check for "special" images that can't be used as
1344 * textures without some extra trickery */
1346 if (!m3gIsPowerOfTwo((M3Guint) width) ||
1347 !m3gIsPowerOfTwo((M3Guint) height)) {
1348 img->special |= IMG_NPOT;
1351 img->powerOfTwo = img;
1354 if (width > M3G_MAX_TEXTURE_DIMENSION ||
1355 height > M3G_MAX_TEXTURE_DIMENSION) {
1356 img->special |= IMG_LARGE;
1360 /* Call base class constructor (can not fail) and return */
1361 m3gInitObject(&img->object, m3g, M3G_CLASS_IMAGE);
1363 M3G_VALIDATE_OBJECT(img);
1364 return (M3GImage) img;
1369 * \brief Prevents further modifications to an image
1371 * Essentially, this changes the default M3G_DYNAMIC flag to
1372 * M3G_STATIC; this allows the implementation to make memory and
1373 * performance optimizations not possible for dynamically modified
1376 M3G_API void m3gCommitImage(M3GImage hImage)
1378 Image *image = (Image *) hImage;
1380 M3G_VALIDATE_OBJECT(image);
1382 flags = image->flags;
1383 flags &= ~(M3G_DYNAMIC|M3G_RENDERING_TARGET);
1384 flags |= M3G_STATIC;
1386 image->flags = flags;
1388 #ifndef M3G_ENABLE_GLES_RESOURCE_HANDLING
1389 /* If the image format has no alpha information, we can discard
1390 * the image data under suitable conditions */
1392 if (image->format == M3G_RGB || image->format == M3G_LUMINANCE) {
1393 image->pinned = M3G_FALSE;
1396 M3G_LOG1(M3G_LOG_IMAGES, "Image 0x%08X made immutable\n",
1401 * \brief Check if image is mutable.
1403 * \param hImage Image object
1404 * \retval M3G_TRUE image is mutable
1405 * \retval M3G_FALSE image is immutable
1407 M3G_API M3Gbool m3gIsMutable(M3GImage hImage)
1409 Image *image = (Image *) hImage;
1410 M3G_VALIDATE_OBJECT(image);
1411 return ((image->flags & M3G_DYNAMIC) != 0);
1415 * \brief Gets image format as JSR-184 constant
1417 * \param hImage Image object
1418 * \return JSR-184 format
1420 M3G_API M3GImageFormat m3gGetFormat(M3GImage hImage)
1422 Image *image = (Image *) hImage;
1423 M3G_VALIDATE_OBJECT(image);
1424 return image->format;
1428 * \brief Gets image width
1430 * \param hImage Image object
1431 * \return width in pixels
1433 M3G_API M3Gint m3gGetWidth(M3GImage hImage)
1435 Image *image = (Image *) hImage;
1436 M3G_VALIDATE_OBJECT(image);
1437 return image->width;
1441 * \brief Gets image height
1443 * \param hImage Image object
1444 * \return height in pixels
1446 M3G_API M3Gint m3gGetHeight(M3GImage hImage)
1448 Image *image = (Image *) hImage;
1449 M3G_VALIDATE_OBJECT(image);
1450 return image->height;
1454 * \brief Converts a rectangle of pixels of src to dst as srcFormat to
1455 * dstFormat conversion requires.
1457 * \param srcFormat source format
1458 * \param src source pixels
1459 * \param srcStride source stride
1460 * \param width width in pixels
1461 * \param height height in pixels
1462 * \param dstFormat destination format
1463 * \param dst destination pixels
1464 * \param dstStride destination stride
1466 static void m3gConvertPixelRect(
1467 M3GPixelFormat srcFormat, const M3Gubyte *src, M3Gsizei srcStride,
1468 M3Gsizei width, M3Gsizei height,
1469 M3GPixelFormat dstFormat, M3Gubyte *dst, M3Gsizei dstStride)
1471 /* Detect any fast path cases */
1473 if ((srcFormat == M3G_BGRA8 || srcFormat == M3G_BGR8_32)
1474 && dstFormat == M3G_RGBA8) {
1475 if (width > 2 && dstStride == width*4) {
1477 const char endianTest[4] = { 1, 0, 0, 0 };
1478 if ((*(const int *)endianTest) == 1) {
1479 fastConvertBGRAToRGBA(src, srcStride, width, height, dst);
1485 /* No luck, do the generic conversion */
1487 while (height-- > 0) {
1488 m3gConvertPixels(srcFormat, src, dstFormat, dst, width);
1495 * \brief Sets the pixel data for an image
1497 * \param hImage Image object
1498 * \param srcPixels source pixels
1500 M3G_API void m3gSetImage(M3GImage hImage, const void *srcPixels)
1502 Image *img = (Image *) hImage;
1503 M3G_VALIDATE_OBJECT(img);
1506 M3Gsizei bpp = m3gBytesPerPixel(m3gInputDataFormat(img));
1507 m3gSetSubImage(hImage,
1508 0, 0, img->width, img->height,
1509 img->width * img->height * bpp, srcPixels);
1514 * \brief Reads pixel data from an image
1516 * \param hImage Image object
1517 * \param pixels output buffer for pixels
1519 M3G_API void m3gGetImageARGB(M3GImage hImage, M3Guint *pixels)
1522 const Image *img = (const Image *) hImage;
1523 M3G_VALIDATE_OBJECT(img);
1524 m3g = M3G_INTERFACE(img);
1527 m3gRaiseError(m3g, M3G_NULL_POINTER);
1532 const M3Gubyte *src = (const M3Gubyte*) m3gMapObject(m3g, img->data);
1533 convertToARGB(img->internalFormat, src,
1534 img->width * img->height,
1536 m3gUnmapObject(m3g, img->data);
1541 * \brief Sets the palette for an image
1543 * \param hImage Image object
1544 * \param paletteLength length of the palette
1545 * \param srcPalette palette data
1547 M3G_API void m3gSetImagePalette(M3GImage hImage,
1548 M3Gint paletteLength,
1549 const void *srcPalette)
1552 Image *img = (Image *) hImage;
1553 M3G_VALIDATE_OBJECT(img);
1554 m3g = M3G_INTERFACE(img);
1556 /* Check for errors */
1558 if (img->data == 0 || (img->flags & M3G_STATIC) != 0
1559 || (img->flags & M3G_PALETTED) == 0) {
1560 M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
1561 m3gRaiseError(m3g, M3G_INVALID_OPERATION);
1564 if (srcPalette == NULL) {
1565 m3gRaiseError(m3g, M3G_NULL_POINTER);
1568 if (!m3gInRange(paletteLength, 0, 256)) {
1569 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1574 * Copy the palette data into the allocated palette (for natively
1575 * supported paletted formats), or remap the existing image data
1576 * using the supplied palette entries (for non-native formats)
1578 * NOTE the latter is a one-time operation!
1580 if (img->paletteBytes > 0) {
1581 M3Gubyte *palette = (M3Gubyte *)m3gMapObject(m3g, img->data);
1582 # if defined(M3G_NGL_TEXTURE_API)
1583 m3gConvertPixels(m3gPixelFormat(img->format), srcPalette,
1587 M3Gsizei bpp = m3gBytesPerPixel(m3gPixelFormat(img->format));
1588 m3gCopy(palette, srcPalette, (size_t) paletteLength * bpp);
1592 M3Gint count = img->width * img->height;
1593 M3Gubyte *pixel = (M3Gubyte*)m3gMapObject(m3g, img->data);
1594 const M3Gubyte *bytePalette = (const M3Gubyte *) srcPalette;
1596 /* We need to treat the input and internal formats as
1597 * separate, as the internal storage may be padded to more
1598 * bytes than there are color components */
1600 M3GPixelFormat paletteFormat = m3gPixelFormat(img->format);
1601 const int numComponents = m3gBytesPerPixel(paletteFormat);
1602 M3GPixelFormat imgFormat = img->internalFormat;
1603 const int imgBpp = m3gBytesPerPixel(imgFormat);
1605 /* In most cases we can just copy the corresponding palette
1606 * entry on top of each pixel based on the pixel intensity (R
1607 * or L component), but special formats require a more
1608 * complicated conversion. We just use the (slow) general
1609 * conversion routine, as it already incorporates support for
1612 if (imgBpp >= numComponents) {
1614 int offset = (*pixel) * numComponents;
1616 for (c = 0; c < numComponents; ++c) {
1617 *pixel++ = bytePalette[offset + c];
1619 while (c++ < imgBpp) { /* padding for e.g. 24-bit RGB */
1626 int offset = (*pixel) * numComponents;
1627 m3gConvertPixels(paletteFormat, &bytePalette[offset],
1634 m3gUnmapObject(m3g, img->data);
1635 m3gInvalidateImage(img);
1639 * \brief Sets a scanline of an image
1641 * \param hImage Image object
1642 * \param line scanline
1643 * \param trueAlpha M3G_TRUE if the source image has an alpha channel,
1644 * M3G_FALSE if it should come from the RGB values;
1645 * this only matters for alpha-only destination images
1646 * \param pixels souce pixels
1648 M3G_API void m3gSetImageScanline(M3GImage hImage,
1651 const M3Guint *pixels)
1653 Image *img = (Image *) hImage;
1654 M3G_VALIDATE_OBJECT(img);
1656 if (img->data == 0 || (img->flags & M3G_STATIC) != 0
1657 || img->paletteBytes != 0) {
1658 m3gRaiseError(M3G_INTERFACE(img), M3G_INVALID_OPERATION);
1663 Interface *m3g = M3G_INTERFACE(img);
1664 M3Gint stride = img->width * m3gBytesPerPixel(img->internalFormat);
1665 M3Gubyte *dst = ((M3Gubyte *) m3gMapObject(m3g, img->data))
1666 + img->paletteBytes;
1668 #ifdef M3G_NGL_TEXTURE_API
1669 /* For RGB images without alpha channel, source alpha is
1670 * forced to 0xff. */
1672 if (img->format == M3G_RGB) {
1676 dst = (M3Guint *) pixels;
1678 for (i = 0; i < img->width; i++) {
1679 argb = *dst | 0xff000000;
1685 /* Note that an alpha-only destination format is faked for
1686 * luminance if the source contained no true alpha data; alpha
1687 * is then inferred from the RGB values instead */
1689 convertFromARGB(pixels,
1691 (img->internalFormat == M3G_A8 && !trueAlpha) ? M3G_L8 : img->internalFormat,
1692 dst + line * stride);
1694 m3gUnmapObject(m3g, img->data);
1695 m3gInvalidateImage(img);
1700 * \brief Sets a rectangular subregion of an image
1702 * \param hImage Image object
1703 * \param x x-coordinate in destination image
1704 * \param y y-coordinate in destination image
1705 * \param width width of source pixels
1706 * \param height height of source pixels
1707 * \param length length of source data, in bytes
1708 * \param pixels source pixels
1710 M3G_API void m3gSetSubImage(M3GImage hImage,
1712 M3Gint width, M3Gint height,
1713 M3Gint length, const void *pixels)
1716 Image *img = (Image *) hImage;
1718 M3GPixelFormat srcFormat;
1721 M3G_VALIDATE_OBJECT(img);
1722 m3g = M3G_INTERFACE(img);
1724 /* Check for errors */
1726 if (img->data == 0 || (img->flags & M3G_STATIC) != 0) {
1727 M3G_ASSERT(!(img->flags & M3G_DYNAMIC));
1728 m3gRaiseError(m3g, M3G_INVALID_OPERATION);
1731 if (pixels == NULL) {
1732 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1735 if (x < 0 || y < 0 || width <= 0 || height <= 0
1736 || x+width > img->width || y+height > img->height) {
1737 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1741 srcFormat = m3gInputDataFormat(img);
1742 srcBpp = m3gBytesPerPixel(srcFormat);
1744 if (length < width * height * srcBpp) {
1745 m3gRaiseError(m3g, M3G_INVALID_VALUE);
1749 /* Copy the image data, doing a conversion if the input format
1750 * does not match the internal storage format */
1752 const M3Gubyte *srcPixels = (const M3Gubyte*) pixels;
1753 M3Gsizei srcStride = width * srcBpp;
1755 M3GPixelFormat dstFormat = img->internalFormat;
1756 M3Gsizei dstBpp = m3gBytesPerPixel(dstFormat);
1757 M3Gsizei dstStride = img->width * dstBpp;
1758 M3Gubyte *dstPixels =
1759 ((M3Gubyte *)m3gMapObject(m3g, img->data))
1761 + y * dstStride + x * dstBpp;
1763 M3Gint numLines = height, numPixels = width;
1764 M3Gbool paletted = (img->flags & M3G_PALETTED) != 0;
1766 /* Optimize the copy for full image width */
1768 if (width == img->width) {
1770 numPixels = width * height;
1773 /* Copy a scanline at a time, converting as necessary */
1775 while (numLines-- > 0) {
1777 /* Matching pixel formats are just copied without
1778 * conversion, and all internally supported paletted
1779 * formats match each other physically, so they can be
1782 if (dstFormat == srcFormat || img->paletteBytes > 0) {
1783 m3gCopy(dstPixels, srcPixels, numPixels * dstBpp);
1788 /* Ordinary conversion into an internal format
1789 * that is encoded differently from the external
1790 * format; can not be a paletted image */
1792 M3G_ASSERT((img->flags & M3G_PALETTED) == 0);
1793 m3gConvertPixels(srcFormat, srcPixels,
1794 dstFormat, dstPixels,
1798 M3G_ASSERT(!m3gSupportedPaletteFormat(img->format));
1800 /* Palette indices for one-byte-per-pixel formats
1801 * are just copied in and mapped to actual values
1802 * later; multibyte paletted formats require a
1803 * conversion into LA, RGB, or RGBA format
1804 * intensity levels temporarily before remapping
1805 * to actual colors in m3gSetImagePalette */
1808 m3gCopy(dstPixels, srcPixels, numPixels);
1811 m3gConvertPixels(M3G_L8, srcPixels,
1812 dstFormat, dstPixels,
1818 srcPixels += srcStride;
1819 dstPixels += dstStride;
1822 /* Release the image data and invalidate mipmap levels */
1824 m3gUnmapObject(m3g, img->data);
1825 m3gInvalidateImage(img);
1827 M3G_VALIDATE_OBJECT(img);
1830 #undef SPAN_BUFFER_SIZE