summaryrefslogtreecommitdiff
path: root/src/glu/mesa/mipmap.c
diff options
context:
space:
mode:
authorjtg <jtg>1999-08-19 00:55:39 +0000
committerjtg <jtg>1999-08-19 00:55:39 +0000
commitafb833d4e89c312460a4ab9ed6a7a8ca4ebbfe1c (patch)
tree59d65b4da12fb5379224cf5f6b808fde91523c7f /src/glu/mesa/mipmap.c
parentf2544d4920ce168bec9cd94d774b7ea5103a3d74 (diff)
Initial revision
Diffstat (limited to 'src/glu/mesa/mipmap.c')
-rw-r--r--src/glu/mesa/mipmap.c790
1 files changed, 790 insertions, 0 deletions
diff --git a/src/glu/mesa/mipmap.c b/src/glu/mesa/mipmap.c
new file mode 100644
index 0000000000..24af0ba33c
--- /dev/null
+++ b/src/glu/mesa/mipmap.c
@@ -0,0 +1,790 @@
+/* $Id: mipmap.c,v 1.1 1999/08/19 00:55:42 jtg Exp $ */
+
+/*
+ * Mesa 3-D graphics library
+ * Version: 3.1
+ * Copyright (C) 1995-1999 Brian Paul
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+/*
+ * $Log: mipmap.c,v $
+ * Revision 1.1 1999/08/19 00:55:42 jtg
+ * Initial revision
+ *
+ * Revision 1.13 1999/03/05 17:49:06 brianp
+ * added support for GL_EXT_abgr (devernay@istar.fr)
+ *
+ * Revision 1.12 1999/01/03 03:23:15 brianp
+ * now using GLAPIENTRY and GLCALLBACK keywords (Ted Jump)
+ *
+ * Revision 1.11 1998/09/18 02:44:03 brianp
+ * further changes to gluScaleImage() per Randy Frank
+ *
+ * Revision 1.10 1998/09/17 03:20:26 brianp
+ * fixed another bug in gluScaleImage() per Sven Panne
+ *
+ * Revision 1.9 1998/07/31 03:06:20 brianp
+ * tweaked the gluScaleImage() function per Randy Frank
+ *
+ * Revision 1.8 1998/07/08 01:02:53 brianp
+ * if gluBuildxDMipmaps() width or height <= 0 return GLU_INVALID_VALUE
+ *
+ * Revision 1.7 1998/07/01 00:18:02 brianp
+ * if gluBuildxDMipmaps() width or height <= 0 just return 0
+ *
+ * Revision 1.6 1998/06/01 01:06:41 brianp
+ * small update for Next/OpenStep from Alexander Mai
+ *
+ * Revision 1.5 1997/07/24 01:28:44 brianp
+ * changed precompiled header symbol from PCH to PC_HEADER
+ *
+ * Revision 1.4 1997/06/23 00:22:56 brianp
+ * added dummy() call to work around an MSVC 4.1 bug
+ *
+ * Revision 1.3 1997/05/28 02:29:38 brianp
+ * added support for precompiled headers (PCH), inserted APIENTRY keyword
+ *
+ * Revision 1.2 1997/05/24 13:32:25 brianp
+ * undef EPSILON in case it's already defined
+ *
+ * Revision 1.1 1996/09/27 01:19:39 brianp
+ * Initial revision
+ *
+ */
+
+
+#ifdef PC_HEADER
+#include "all.h"
+#else
+#include <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "gluP.h"
+#endif
+
+
+/*
+ * Compute ceiling of integer quotient of A divided by B:
+ */
+#define CEILING( A, B ) ( (A) % (B) == 0 ? (A)/(B) : (A)/(B)+1 )
+
+
+
+#ifdef EPSILON
+#undef EPSILON
+#endif
+#define EPSILON 0.001
+
+
+/* To work around optimizer bug in MSVC4.1 */
+#if defined(__WIN32__) && !defined(OPENSTEP)
+void dummy(GLuint j, GLuint k){
+}
+#else
+#define dummy(J, K)
+#endif
+
+
+GLint GLAPIENTRY gluScaleImage( GLenum format,
+ GLint widthin, GLint heightin,
+ GLenum typein, const void *datain,
+ GLint widthout, GLint heightout,
+ GLenum typeout, void *dataout )
+{
+ GLint components, i, j, k;
+ GLfloat *tempin, *tempout;
+ GLfloat sx, sy;
+ GLint unpackrowlength, unpackalignment, unpackskiprows, unpackskippixels;
+ GLint packrowlength, packalignment, packskiprows, packskippixels;
+ GLint sizein, sizeout;
+ GLint rowstride, rowlen;
+
+
+ /* Determine number of components per pixel */
+ switch (format) {
+ case GL_COLOR_INDEX:
+ case GL_STENCIL_INDEX:
+ case GL_DEPTH_COMPONENT:
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ components = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ components = 2;
+ break;
+ case GL_RGB:
+ components = 3;
+ break;
+ case GL_RGBA:
+#ifdef GL_EXT_abgr
+ case GL_ABGR_EXT:
+#endif
+ components = 4;
+ break;
+ default:
+ return GLU_INVALID_ENUM;
+ }
+
+ /* Determine bytes per input datum */
+ switch (typein) {
+ case GL_UNSIGNED_BYTE: sizein = sizeof(GLubyte); break;
+ case GL_BYTE: sizein = sizeof(GLbyte); break;
+ case GL_UNSIGNED_SHORT: sizein = sizeof(GLushort); break;
+ case GL_SHORT: sizein = sizeof(GLshort); break;
+ case GL_UNSIGNED_INT: sizein = sizeof(GLuint); break;
+ case GL_INT: sizein = sizeof(GLint); break;
+ case GL_FLOAT: sizein = sizeof(GLfloat); break;
+ case GL_BITMAP:
+ /* not implemented yet */
+ default:
+ return GL_INVALID_ENUM;
+ }
+
+ /* Determine bytes per output datum */
+ switch (typeout) {
+ case GL_UNSIGNED_BYTE: sizeout = sizeof(GLubyte); break;
+ case GL_BYTE: sizeout = sizeof(GLbyte); break;
+ case GL_UNSIGNED_SHORT: sizeout = sizeof(GLushort); break;
+ case GL_SHORT: sizeout = sizeof(GLshort); break;
+ case GL_UNSIGNED_INT: sizeout = sizeof(GLuint); break;
+ case GL_INT: sizeout = sizeof(GLint); break;
+ case GL_FLOAT: sizeout = sizeof(GLfloat); break;
+ case GL_BITMAP:
+ /* not implemented yet */
+ default:
+ return GL_INVALID_ENUM;
+ }
+
+ /* Get glPixelStore state */
+ glGetIntegerv( GL_UNPACK_ROW_LENGTH, &unpackrowlength );
+ glGetIntegerv( GL_UNPACK_ALIGNMENT, &unpackalignment );
+ glGetIntegerv( GL_UNPACK_SKIP_ROWS, &unpackskiprows );
+ glGetIntegerv( GL_UNPACK_SKIP_PIXELS, &unpackskippixels );
+ glGetIntegerv( GL_PACK_ROW_LENGTH, &packrowlength );
+ glGetIntegerv( GL_PACK_ALIGNMENT, &packalignment );
+ glGetIntegerv( GL_PACK_SKIP_ROWS, &packskiprows );
+ glGetIntegerv( GL_PACK_SKIP_PIXELS, &packskippixels );
+
+ /* Allocate storage for intermediate images */
+ tempin = (GLfloat *) malloc( widthin * heightin
+ * components * sizeof(GLfloat) );
+ if (!tempin) {
+ return GLU_OUT_OF_MEMORY;
+ }
+ tempout = (GLfloat *) malloc( widthout * heightout
+ * components * sizeof(GLfloat) );
+ if (!tempout) {
+ free( tempin );
+ return GLU_OUT_OF_MEMORY;
+ }
+
+
+ /*
+ * Unpack the pixel data and convert to floating point
+ */
+
+ if (unpackrowlength>0) {
+ rowlen = unpackrowlength;
+ }
+ else {
+ rowlen = widthin;
+ }
+ if (sizein >= unpackalignment) {
+ rowstride = components * rowlen;
+ }
+ else {
+ rowstride = unpackalignment/sizein
+ * CEILING( components * rowlen * sizein, unpackalignment );
+ }
+
+ switch (typein) {
+ case GL_UNSIGNED_BYTE:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLubyte *ubptr = (GLubyte *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *ubptr++;
+ }
+ }
+ break;
+ case GL_BYTE:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLbyte *bptr = (GLbyte *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *bptr++;
+ }
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLushort *usptr = (GLushort *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *usptr++;
+ }
+ }
+ break;
+ case GL_SHORT:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLshort *sptr = (GLshort *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *sptr++;
+ }
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLuint *uiptr = (GLuint *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *uiptr++;
+ }
+ }
+ break;
+ case GL_INT:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLint *iptr = (GLint *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = (GLfloat) *iptr++;
+ }
+ }
+ break;
+ case GL_FLOAT:
+ k = 0;
+ for (i=0;i<heightin;i++) {
+ GLfloat *fptr = (GLfloat *) datain
+ + i * rowstride
+ + unpackskiprows * rowstride
+ + unpackskippixels * components;
+ for (j=0;j<widthin*components;j++) {
+ dummy(j, k);
+ tempin[k++] = *fptr++;
+ }
+ }
+ break;
+ default:
+ return GLU_INVALID_ENUM;
+ }
+
+
+ /*
+ * Scale the image!
+ */
+
+ if (widthout > 1)
+ sx = (GLfloat) (widthin-1) / (GLfloat) (widthout-1);
+ else
+ sx = (GLfloat) (widthin-1);
+ if (heightout > 1)
+ sy = (GLfloat) (heightin-1) / (GLfloat) (heightout-1);
+ else
+ sy = (GLfloat) (heightin-1);
+
+/*#define POINT_SAMPLE*/
+#ifdef POINT_SAMPLE
+ for (i=0;i<heightout;i++) {
+ GLint ii = i * sy;
+ for (j=0;j<widthout;j++) {
+ GLint jj = j * sx;
+
+ GLfloat *src = tempin + (ii * widthin + jj) * components;
+ GLfloat *dst = tempout + (i * widthout + j) * components;
+
+ for (k=0;k<components;k++) {
+ *dst++ = *src++;
+ }
+ }
+ }
+#else
+ if (sx<1.0 && sy<1.0) {
+ /* magnify both width and height: use weighted sample of 4 pixels */
+ GLint i0, i1, j0, j1;
+ GLfloat alpha, beta;
+ GLfloat *src00, *src01, *src10, *src11;
+ GLfloat s1, s2;
+ GLfloat *dst;
+
+ for (i=0;i<heightout;i++) {
+ i0 = i * sy;
+ i1 = i0 + 1;
+ if (i1 >= heightin) i1 = heightin-1;
+/* i1 = (i+1) * sy - EPSILON;*/
+ alpha = i*sy - i0;
+ for (j=0;j<widthout;j++) {
+ j0 = j * sx;
+ j1 = j0 + 1;
+ if (j1 >= widthin) j1 = widthin-1;
+/* j1 = (j+1) * sx - EPSILON; */
+ beta = j*sx - j0;
+
+ /* compute weighted average of pixels in rect (i0,j0)-(i1,j1) */
+ src00 = tempin + (i0 * widthin + j0) * components;
+ src01 = tempin + (i0 * widthin + j1) * components;
+ src10 = tempin + (i1 * widthin + j0) * components;
+ src11 = tempin + (i1 * widthin + j1) * components;
+
+ dst = tempout + (i * widthout + j) * components;
+
+ for (k=0;k<components;k++) {
+ s1 = *src00++ * (1.0-beta) + *src01++ * beta;
+ s2 = *src10++ * (1.0-beta) + *src11++ * beta;
+ *dst++ = s1 * (1.0-alpha) + s2 * alpha;
+ }
+ }
+ }
+ }
+ else {
+ /* shrink width and/or height: use an unweighted box filter */
+ GLint i0, i1;
+ GLint j0, j1;
+ GLint ii, jj;
+ GLfloat sum, *dst;
+
+ for (i=0;i<heightout;i++) {
+ i0 = i * sy;
+ i1 = i0 + 1;
+ if (i1 >= heightin) i1 = heightin-1;
+/* i1 = (i+1) * sy - EPSILON; */
+ for (j=0;j<widthout;j++) {
+ j0 = j * sx;
+ j1 = j0 + 1;
+ if (j1 >= widthin) j1 = widthin-1;
+/* j1 = (j+1) * sx - EPSILON; */
+
+ dst = tempout + (i * widthout + j) * components;
+
+ /* compute average of pixels in the rectangle (i0,j0)-(i1,j1) */
+ for (k=0;k<components;k++) {
+ sum = 0.0;
+ for (ii=i0;ii<=i1;ii++) {
+ for (jj=j0;jj<=j1;jj++) {
+ sum += *(tempin + (ii * widthin + jj) * components + k);
+ }
+ }
+ sum /= (j1-j0+1) * (i1-i0+1);
+ *dst++ = sum;
+ }
+ }
+ }
+ }
+#endif
+
+
+ /*
+ * Return output image
+ */
+
+ if (packrowlength>0) {
+ rowlen = packrowlength;
+ }
+ else {
+ rowlen = widthout;
+ }
+ if (sizeout >= packalignment) {
+ rowstride = components * rowlen;
+ }
+ else {
+ rowstride = packalignment/sizeout
+ * CEILING( components * rowlen * sizeout, packalignment );
+ }
+
+ switch (typeout) {
+ case GL_UNSIGNED_BYTE:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLubyte *ubptr = (GLubyte *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *ubptr++ = (GLubyte) tempout[k++];
+ }
+ }
+ break;
+ case GL_BYTE:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLbyte *bptr = (GLbyte *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *bptr++ = (GLbyte) tempout[k++];
+ }
+ }
+ break;
+ case GL_UNSIGNED_SHORT:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLushort *usptr = (GLushort *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *usptr++ = (GLushort) tempout[k++];
+ }
+ }
+ break;
+ case GL_SHORT:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLshort *sptr = (GLshort *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *sptr++ = (GLshort) tempout[k++];
+ }
+ }
+ break;
+ case GL_UNSIGNED_INT:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLuint *uiptr = (GLuint *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *uiptr++ = (GLuint) tempout[k++];
+ }
+ }
+ break;
+ case GL_INT:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLint *iptr = (GLint *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *iptr++ = (GLint) tempout[k++];
+ }
+ }
+ break;
+ case GL_FLOAT:
+ k = 0;
+ for (i=0;i<heightout;i++) {
+ GLfloat *fptr = (GLfloat *) dataout
+ + i * rowstride
+ + packskiprows * rowstride
+ + packskippixels * components;
+ for (j=0;j<widthout*components;j++) {
+ dummy(j, k+i);
+ *fptr++ = tempout[k++];
+ }
+ }
+ break;
+ default:
+ return GLU_INVALID_ENUM;
+ }
+
+
+ /* free temporary image storage */
+ free( tempin );
+ free( tempout );
+
+ return 0;
+}
+
+
+
+/*
+ * Return the largest k such that 2^k <= n.
+ */
+static GLint ilog2( GLint n )
+{
+ GLint k;
+
+ if (n<=0) return 0;
+ for (k=0; n>>=1; k++) ;
+ return k;
+}
+
+
+
+/*
+ * Find the value nearest to n which is also a power of two.
+ */
+static GLint round2( GLint n )
+{
+ GLint m;
+
+ for (m=1; m<n; m*=2)
+ ;
+
+ /* m>=n */
+ if (m-n <= n-m/2) {
+ return m;
+ }
+ else {
+ return m/2;
+ }
+}
+
+
+/*
+ * Given an pixel format and datatype, return the number of bytes to
+ * store one pixel.
+ */
+static GLint bytes_per_pixel( GLenum format, GLenum type )
+{
+ GLint n, m;
+
+ switch (format) {
+ case GL_COLOR_INDEX:
+ case GL_STENCIL_INDEX:
+ case GL_DEPTH_COMPONENT:
+ case GL_RED:
+ case GL_GREEN:
+ case GL_BLUE:
+ case GL_ALPHA:
+ case GL_LUMINANCE:
+ n = 1;
+ break;
+ case GL_LUMINANCE_ALPHA:
+ n = 2;
+ break;
+ case GL_RGB:
+ n = 3;
+ break;
+ case GL_RGBA:
+#ifdef GL_EXT_abgr
+ case GL_ABGR_EXT:
+#endif
+ n = 4;
+ break;
+ default:
+ n = 0;
+ }
+
+ switch (type) {
+ case GL_UNSIGNED_BYTE: m = sizeof(GLubyte); break;
+ case GL_BYTE: m = sizeof(GLbyte); break;
+ case GL_BITMAP: m = 1; break;
+ case GL_UNSIGNED_SHORT: m = sizeof(GLushort); break;
+ case GL_SHORT: m = sizeof(GLshort); break;
+ case GL_UNSIGNED_INT: m = sizeof(GLuint); break;
+ case GL_INT: m = sizeof(GLint); break;
+ case GL_FLOAT: m = sizeof(GLfloat); break;
+ default: m = 0;
+ }
+
+ return n * m;
+}
+
+
+
+/*
+ * WARNING: This function isn't finished and has never been tested!!!!
+ */
+GLint GLAPIENTRY gluBuild1DMipmaps( GLenum target, GLint components,
+ GLint width, GLenum format,
+ GLenum type, const void *data )
+{
+ GLubyte *texture;
+ GLint levels, max_levels;
+ GLint new_width, max_width;
+ GLint i, j, k, l;
+
+ if (width < 1)
+ return GLU_INVALID_VALUE;
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_width );
+ max_levels = ilog2( max_width ) + 1;
+
+ /* Compute how many mipmap images to make */
+ levels = ilog2( width ) + 1;
+ if (levels>max_levels) {
+ levels = max_levels;
+ }
+
+ new_width = 1 << (levels-1);
+
+ texture = (GLubyte *) malloc( new_width * components );
+ if (!texture) {
+ return GLU_OUT_OF_MEMORY;
+ }
+
+ if (width != new_width) {
+ /* initial rescaling */
+ switch (type) {
+ case GL_UNSIGNED_BYTE:
+ {
+ GLubyte *ub_data = (GLubyte *) data;
+ for (i=0;i<new_width;i++) {
+ j = i * width / new_width;
+ for (k=0;k<components;k++) {
+ texture[i*components+k] = ub_data[j*components+k];
+ }
+ }
+ }
+ break;
+ default:
+ /* Not implemented */
+ return GLU_ERROR;
+ }
+ }
+
+ /* generate and load mipmap images */
+ for (l=0;l<levels;l++) {
+ glTexImage1D( GL_TEXTURE_1D, l, components, new_width, 0,
+ format, GL_UNSIGNED_BYTE, texture );
+
+ /* Scale image down to 1/2 size */
+ new_width = new_width / 2;
+ for (i=0;i<new_width;i++) {
+ for (k=0;k<components;k++) {
+ GLint sample1, sample2;
+ sample1 = (GLint) texture[i*2*components+k];
+ sample2 = (GLint) texture[(i*2+1)*components+k];
+ texture[i*components+k] = (GLubyte) ((sample1 + sample2) / 2);
+ }
+ }
+ }
+
+ free( texture );
+
+ /* make sure remaining mipmap levels are removed */
+ for (l=levels;l<max_levels;l++) {
+ glTexImage1D( GL_TEXTURE_1D, l, components, 0, 0,
+ format, GL_UNSIGNED_BYTE, NULL );
+ }
+
+ return 0;
+}
+
+
+
+GLint GLAPIENTRY gluBuild2DMipmaps( GLenum target, GLint components,
+ GLint width, GLint height, GLenum format,
+ GLenum type, const void *data )
+{
+ GLint w, h, maxsize;
+ void *image, *newimage;
+ GLint neww, newh, level, bpp;
+ int error;
+
+ if (width < 1 || height < 1)
+ return GLU_INVALID_VALUE;
+
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxsize );
+
+ w = round2( width );
+ if (w>maxsize) {
+ w = maxsize;
+ }
+ h = round2( height );
+ if (h>maxsize) {
+ h = maxsize;
+ }
+
+ bpp = bytes_per_pixel( format, type );
+ if (bpp==0) {
+ /* probably a bad format or type enum */
+ return GLU_INVALID_ENUM;
+ }
+
+ if (w!=width || h!=height) {
+ /* must rescale image to get "top" mipmap texture image */
+ image = malloc( (w+4) * h * bpp );
+ if (!image) {
+ return GLU_OUT_OF_MEMORY;
+ }
+ error = gluScaleImage( format, width, height, type, data,
+ w, h, type, image );
+ if (error) {
+ return error;
+ }
+ }
+ else {
+ image = (void *) data;
+ }
+
+ level = 0;
+ while (1) {
+ glTexImage2D( target, level, components, w, h, 0, format, type, image );
+
+ if (w==1 && h==1) break;
+
+ neww = (w<2) ? 1 : w/2;
+ newh = (h<2) ? 1 : h/2;
+ newimage = malloc( (neww+4) * newh * bpp );
+ if (!newimage) {
+ return GLU_OUT_OF_MEMORY;
+ }
+
+ error = gluScaleImage( format, w, h, type, image,
+ neww, newh, type, newimage );
+ if (error) {
+ return error;
+ }
+
+ if (image!=data) {
+ free( image );
+ }
+ image = newimage;
+
+ w = neww;
+ h = newh;
+ level++;
+ }
+
+ if (image!=data) {
+ free( image );
+ }
+
+ return 0;
+}
+