/* $Id: mipmap.c,v 1.2 2003-08-22 20:11:43 brianp Exp $ */ /* * Mesa 3-D graphics library * Version: 3.4 * Copyright (C) 1995-2000 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. */ #ifdef PC_HEADER #include "all.h" #else #include #include #include #include #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, GLsizei widthin, GLsizei heightin, GLenum typein, const void *datain, GLsizei widthout, GLsizei heightout, GLenum typeout, void *dataout) { GLint components, i, j, k; GLfloat *tempin, *tempout, f; 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: case GL_BGR: components = 3; break; case GL_RGBA: case GL_BGRA: #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 */ glGetFloatv(GL_UNPACK_ROW_LENGTH, &f); unpackrowlength = (int)f; glGetFloatv(GL_UNPACK_ALIGNMENT, &f); unpackalignment = (int)f; glGetFloatv(GL_UNPACK_SKIP_ROWS, &f); unpackskiprows = (int)f; glGetFloatv(GL_UNPACK_SKIP_PIXELS, &f); unpackskippixels = (int)f; glGetFloatv(GL_PACK_ROW_LENGTH, &f); packrowlength = (int)f; glGetFloatv(GL_PACK_ALIGNMENT, &f); packalignment = (int)f; glGetFloatv(GL_PACK_SKIP_ROWS, &f); packskiprows = (int)f; glGetFloatv(GL_PACK_SKIP_PIXELS, &f); packskippixels = (int)f; /* 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 data type, 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: case GL_BGR: n = 3; break; case GL_RGBA: case GL_BGRA: #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, GLsizei width, GLenum format, GLenum type, const void *data) { return 0; } GLint GLAPIENTRY gluBuild2DMipmaps(GLenum target, GLint components, GLsizei width, GLsizei height, GLenum format, GLenum type, const void *data) { GLint w, h; GLint maxsize; void *image, *newimage; GLint neww, newh, level, bpp; int error; GLboolean done; GLint retval = 0; GLint unpackrowlength, unpackalignment, unpackskiprows, unpackskippixels; GLint packrowlength, packalignment, packskiprows, packskippixels; GLfloat f; if (width < 1 || height < 1) return GLU_INVALID_VALUE; glGetFloatv(GL_MAX_TEXTURE_SIZE, &f); maxsize = (int)f; 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; } /* Get current glPixelStore values */ glGetFloatv(GL_UNPACK_ROW_LENGTH, &f); unpackrowlength = (int)f; glGetFloatv(GL_UNPACK_ALIGNMENT, &f); unpackalignment = (int)f; glGetFloatv(GL_UNPACK_SKIP_ROWS, &f); unpackskiprows = (int)f; glGetFloatv(GL_UNPACK_SKIP_PIXELS, &f); unpackskippixels = (int)f; glGetFloatv(GL_PACK_ROW_LENGTH, &f); packrowlength = (int)f; glGetFloatv(GL_PACK_ALIGNMENT, &f); packalignment = (int)f; glGetFloatv(GL_PACK_SKIP_ROWS, &f); packskiprows = (int)f; glGetFloatv(GL_PACK_SKIP_PIXELS, &f); packskippixels = (int)f; /* set pixel packing */ glPixelStorei(GL_PACK_ROW_LENGTH, 0); glPixelStorei(GL_PACK_ALIGNMENT, 1); glPixelStorei(GL_PACK_SKIP_ROWS, 0); glPixelStorei(GL_PACK_SKIP_PIXELS, 0); done = GL_FALSE; 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) { retval = error; done = GL_TRUE; } } else { image = (void *) data; } level = 0; while (!done) { if (image != data) { /* set pixel unpacking */ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glPixelStorei(GL_UNPACK_SKIP_ROWS, 0); glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0); } 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) { retval = error; done = GL_TRUE; } if (image != data) { free(image); } image = newimage; w = neww; h = newh; level++; } if (image != data) { free(image); } /* Restore original glPixelStore state */ glPixelStorei(GL_UNPACK_ROW_LENGTH, unpackrowlength); glPixelStorei(GL_UNPACK_ALIGNMENT, unpackalignment); glPixelStorei(GL_UNPACK_SKIP_ROWS, unpackskiprows); glPixelStorei(GL_UNPACK_SKIP_PIXELS, unpackskippixels); glPixelStorei(GL_PACK_ROW_LENGTH, packrowlength); glPixelStorei(GL_PACK_ALIGNMENT, packalignment); glPixelStorei(GL_PACK_SKIP_ROWS, packskiprows); glPixelStorei(GL_PACK_SKIP_PIXELS, packskippixels); return retval; }