Initial revision
[mesa.git] / src / glu / mesa / mipmap.c
1 /* $Id: mipmap.c,v 1.1 1999/08/19 00:55:42 jtg Exp $ */
2
3 /*
4 * Mesa 3-D graphics library
5 * Version: 3.1
6 * Copyright (C) 1995-1999 Brian Paul
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public
19 * License along with this library; if not, write to the Free
20 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23
24 /*
25 * $Log: mipmap.c,v $
26 * Revision 1.1 1999/08/19 00:55:42 jtg
27 * Initial revision
28 *
29 * Revision 1.13 1999/03/05 17:49:06 brianp
30 * added support for GL_EXT_abgr (devernay@istar.fr)
31 *
32 * Revision 1.12 1999/01/03 03:23:15 brianp
33 * now using GLAPIENTRY and GLCALLBACK keywords (Ted Jump)
34 *
35 * Revision 1.11 1998/09/18 02:44:03 brianp
36 * further changes to gluScaleImage() per Randy Frank
37 *
38 * Revision 1.10 1998/09/17 03:20:26 brianp
39 * fixed another bug in gluScaleImage() per Sven Panne
40 *
41 * Revision 1.9 1998/07/31 03:06:20 brianp
42 * tweaked the gluScaleImage() function per Randy Frank
43 *
44 * Revision 1.8 1998/07/08 01:02:53 brianp
45 * if gluBuildxDMipmaps() width or height <= 0 return GLU_INVALID_VALUE
46 *
47 * Revision 1.7 1998/07/01 00:18:02 brianp
48 * if gluBuildxDMipmaps() width or height <= 0 just return 0
49 *
50 * Revision 1.6 1998/06/01 01:06:41 brianp
51 * small update for Next/OpenStep from Alexander Mai
52 *
53 * Revision 1.5 1997/07/24 01:28:44 brianp
54 * changed precompiled header symbol from PCH to PC_HEADER
55 *
56 * Revision 1.4 1997/06/23 00:22:56 brianp
57 * added dummy() call to work around an MSVC 4.1 bug
58 *
59 * Revision 1.3 1997/05/28 02:29:38 brianp
60 * added support for precompiled headers (PCH), inserted APIENTRY keyword
61 *
62 * Revision 1.2 1997/05/24 13:32:25 brianp
63 * undef EPSILON in case it's already defined
64 *
65 * Revision 1.1 1996/09/27 01:19:39 brianp
66 * Initial revision
67 *
68 */
69
70
71 #ifdef PC_HEADER
72 #include "all.h"
73 #else
74 #include <assert.h>
75 #include <math.h>
76 #include <stdio.h>
77 #include <stdlib.h>
78 #include "gluP.h"
79 #endif
80
81
82 /*
83 * Compute ceiling of integer quotient of A divided by B:
84 */
85 #define CEILING( A, B ) ( (A) % (B) == 0 ? (A)/(B) : (A)/(B)+1 )
86
87
88
89 #ifdef EPSILON
90 #undef EPSILON
91 #endif
92 #define EPSILON 0.001
93
94
95 /* To work around optimizer bug in MSVC4.1 */
96 #if defined(__WIN32__) && !defined(OPENSTEP)
97 void dummy(GLuint j, GLuint k){
98 }
99 #else
100 #define dummy(J, K)
101 #endif
102
103
104 GLint GLAPIENTRY gluScaleImage( GLenum format,
105 GLint widthin, GLint heightin,
106 GLenum typein, const void *datain,
107 GLint widthout, GLint heightout,
108 GLenum typeout, void *dataout )
109 {
110 GLint components, i, j, k;
111 GLfloat *tempin, *tempout;
112 GLfloat sx, sy;
113 GLint unpackrowlength, unpackalignment, unpackskiprows, unpackskippixels;
114 GLint packrowlength, packalignment, packskiprows, packskippixels;
115 GLint sizein, sizeout;
116 GLint rowstride, rowlen;
117
118
119 /* Determine number of components per pixel */
120 switch (format) {
121 case GL_COLOR_INDEX:
122 case GL_STENCIL_INDEX:
123 case GL_DEPTH_COMPONENT:
124 case GL_RED:
125 case GL_GREEN:
126 case GL_BLUE:
127 case GL_ALPHA:
128 case GL_LUMINANCE:
129 components = 1;
130 break;
131 case GL_LUMINANCE_ALPHA:
132 components = 2;
133 break;
134 case GL_RGB:
135 components = 3;
136 break;
137 case GL_RGBA:
138 #ifdef GL_EXT_abgr
139 case GL_ABGR_EXT:
140 #endif
141 components = 4;
142 break;
143 default:
144 return GLU_INVALID_ENUM;
145 }
146
147 /* Determine bytes per input datum */
148 switch (typein) {
149 case GL_UNSIGNED_BYTE: sizein = sizeof(GLubyte); break;
150 case GL_BYTE: sizein = sizeof(GLbyte); break;
151 case GL_UNSIGNED_SHORT: sizein = sizeof(GLushort); break;
152 case GL_SHORT: sizein = sizeof(GLshort); break;
153 case GL_UNSIGNED_INT: sizein = sizeof(GLuint); break;
154 case GL_INT: sizein = sizeof(GLint); break;
155 case GL_FLOAT: sizein = sizeof(GLfloat); break;
156 case GL_BITMAP:
157 /* not implemented yet */
158 default:
159 return GL_INVALID_ENUM;
160 }
161
162 /* Determine bytes per output datum */
163 switch (typeout) {
164 case GL_UNSIGNED_BYTE: sizeout = sizeof(GLubyte); break;
165 case GL_BYTE: sizeout = sizeof(GLbyte); break;
166 case GL_UNSIGNED_SHORT: sizeout = sizeof(GLushort); break;
167 case GL_SHORT: sizeout = sizeof(GLshort); break;
168 case GL_UNSIGNED_INT: sizeout = sizeof(GLuint); break;
169 case GL_INT: sizeout = sizeof(GLint); break;
170 case GL_FLOAT: sizeout = sizeof(GLfloat); break;
171 case GL_BITMAP:
172 /* not implemented yet */
173 default:
174 return GL_INVALID_ENUM;
175 }
176
177 /* Get glPixelStore state */
178 glGetIntegerv( GL_UNPACK_ROW_LENGTH, &unpackrowlength );
179 glGetIntegerv( GL_UNPACK_ALIGNMENT, &unpackalignment );
180 glGetIntegerv( GL_UNPACK_SKIP_ROWS, &unpackskiprows );
181 glGetIntegerv( GL_UNPACK_SKIP_PIXELS, &unpackskippixels );
182 glGetIntegerv( GL_PACK_ROW_LENGTH, &packrowlength );
183 glGetIntegerv( GL_PACK_ALIGNMENT, &packalignment );
184 glGetIntegerv( GL_PACK_SKIP_ROWS, &packskiprows );
185 glGetIntegerv( GL_PACK_SKIP_PIXELS, &packskippixels );
186
187 /* Allocate storage for intermediate images */
188 tempin = (GLfloat *) malloc( widthin * heightin
189 * components * sizeof(GLfloat) );
190 if (!tempin) {
191 return GLU_OUT_OF_MEMORY;
192 }
193 tempout = (GLfloat *) malloc( widthout * heightout
194 * components * sizeof(GLfloat) );
195 if (!tempout) {
196 free( tempin );
197 return GLU_OUT_OF_MEMORY;
198 }
199
200
201 /*
202 * Unpack the pixel data and convert to floating point
203 */
204
205 if (unpackrowlength>0) {
206 rowlen = unpackrowlength;
207 }
208 else {
209 rowlen = widthin;
210 }
211 if (sizein >= unpackalignment) {
212 rowstride = components * rowlen;
213 }
214 else {
215 rowstride = unpackalignment/sizein
216 * CEILING( components * rowlen * sizein, unpackalignment );
217 }
218
219 switch (typein) {
220 case GL_UNSIGNED_BYTE:
221 k = 0;
222 for (i=0;i<heightin;i++) {
223 GLubyte *ubptr = (GLubyte *) datain
224 + i * rowstride
225 + unpackskiprows * rowstride
226 + unpackskippixels * components;
227 for (j=0;j<widthin*components;j++) {
228 dummy(j, k);
229 tempin[k++] = (GLfloat) *ubptr++;
230 }
231 }
232 break;
233 case GL_BYTE:
234 k = 0;
235 for (i=0;i<heightin;i++) {
236 GLbyte *bptr = (GLbyte *) datain
237 + i * rowstride
238 + unpackskiprows * rowstride
239 + unpackskippixels * components;
240 for (j=0;j<widthin*components;j++) {
241 dummy(j, k);
242 tempin[k++] = (GLfloat) *bptr++;
243 }
244 }
245 break;
246 case GL_UNSIGNED_SHORT:
247 k = 0;
248 for (i=0;i<heightin;i++) {
249 GLushort *usptr = (GLushort *) datain
250 + i * rowstride
251 + unpackskiprows * rowstride
252 + unpackskippixels * components;
253 for (j=0;j<widthin*components;j++) {
254 dummy(j, k);
255 tempin[k++] = (GLfloat) *usptr++;
256 }
257 }
258 break;
259 case GL_SHORT:
260 k = 0;
261 for (i=0;i<heightin;i++) {
262 GLshort *sptr = (GLshort *) datain
263 + i * rowstride
264 + unpackskiprows * rowstride
265 + unpackskippixels * components;
266 for (j=0;j<widthin*components;j++) {
267 dummy(j, k);
268 tempin[k++] = (GLfloat) *sptr++;
269 }
270 }
271 break;
272 case GL_UNSIGNED_INT:
273 k = 0;
274 for (i=0;i<heightin;i++) {
275 GLuint *uiptr = (GLuint *) datain
276 + i * rowstride
277 + unpackskiprows * rowstride
278 + unpackskippixels * components;
279 for (j=0;j<widthin*components;j++) {
280 dummy(j, k);
281 tempin[k++] = (GLfloat) *uiptr++;
282 }
283 }
284 break;
285 case GL_INT:
286 k = 0;
287 for (i=0;i<heightin;i++) {
288 GLint *iptr = (GLint *) datain
289 + i * rowstride
290 + unpackskiprows * rowstride
291 + unpackskippixels * components;
292 for (j=0;j<widthin*components;j++) {
293 dummy(j, k);
294 tempin[k++] = (GLfloat) *iptr++;
295 }
296 }
297 break;
298 case GL_FLOAT:
299 k = 0;
300 for (i=0;i<heightin;i++) {
301 GLfloat *fptr = (GLfloat *) datain
302 + i * rowstride
303 + unpackskiprows * rowstride
304 + unpackskippixels * components;
305 for (j=0;j<widthin*components;j++) {
306 dummy(j, k);
307 tempin[k++] = *fptr++;
308 }
309 }
310 break;
311 default:
312 return GLU_INVALID_ENUM;
313 }
314
315
316 /*
317 * Scale the image!
318 */
319
320 if (widthout > 1)
321 sx = (GLfloat) (widthin-1) / (GLfloat) (widthout-1);
322 else
323 sx = (GLfloat) (widthin-1);
324 if (heightout > 1)
325 sy = (GLfloat) (heightin-1) / (GLfloat) (heightout-1);
326 else
327 sy = (GLfloat) (heightin-1);
328
329 /*#define POINT_SAMPLE*/
330 #ifdef POINT_SAMPLE
331 for (i=0;i<heightout;i++) {
332 GLint ii = i * sy;
333 for (j=0;j<widthout;j++) {
334 GLint jj = j * sx;
335
336 GLfloat *src = tempin + (ii * widthin + jj) * components;
337 GLfloat *dst = tempout + (i * widthout + j) * components;
338
339 for (k=0;k<components;k++) {
340 *dst++ = *src++;
341 }
342 }
343 }
344 #else
345 if (sx<1.0 && sy<1.0) {
346 /* magnify both width and height: use weighted sample of 4 pixels */
347 GLint i0, i1, j0, j1;
348 GLfloat alpha, beta;
349 GLfloat *src00, *src01, *src10, *src11;
350 GLfloat s1, s2;
351 GLfloat *dst;
352
353 for (i=0;i<heightout;i++) {
354 i0 = i * sy;
355 i1 = i0 + 1;
356 if (i1 >= heightin) i1 = heightin-1;
357 /* i1 = (i+1) * sy - EPSILON;*/
358 alpha = i*sy - i0;
359 for (j=0;j<widthout;j++) {
360 j0 = j * sx;
361 j1 = j0 + 1;
362 if (j1 >= widthin) j1 = widthin-1;
363 /* j1 = (j+1) * sx - EPSILON; */
364 beta = j*sx - j0;
365
366 /* compute weighted average of pixels in rect (i0,j0)-(i1,j1) */
367 src00 = tempin + (i0 * widthin + j0) * components;
368 src01 = tempin + (i0 * widthin + j1) * components;
369 src10 = tempin + (i1 * widthin + j0) * components;
370 src11 = tempin + (i1 * widthin + j1) * components;
371
372 dst = tempout + (i * widthout + j) * components;
373
374 for (k=0;k<components;k++) {
375 s1 = *src00++ * (1.0-beta) + *src01++ * beta;
376 s2 = *src10++ * (1.0-beta) + *src11++ * beta;
377 *dst++ = s1 * (1.0-alpha) + s2 * alpha;
378 }
379 }
380 }
381 }
382 else {
383 /* shrink width and/or height: use an unweighted box filter */
384 GLint i0, i1;
385 GLint j0, j1;
386 GLint ii, jj;
387 GLfloat sum, *dst;
388
389 for (i=0;i<heightout;i++) {
390 i0 = i * sy;
391 i1 = i0 + 1;
392 if (i1 >= heightin) i1 = heightin-1;
393 /* i1 = (i+1) * sy - EPSILON; */
394 for (j=0;j<widthout;j++) {
395 j0 = j * sx;
396 j1 = j0 + 1;
397 if (j1 >= widthin) j1 = widthin-1;
398 /* j1 = (j+1) * sx - EPSILON; */
399
400 dst = tempout + (i * widthout + j) * components;
401
402 /* compute average of pixels in the rectangle (i0,j0)-(i1,j1) */
403 for (k=0;k<components;k++) {
404 sum = 0.0;
405 for (ii=i0;ii<=i1;ii++) {
406 for (jj=j0;jj<=j1;jj++) {
407 sum += *(tempin + (ii * widthin + jj) * components + k);
408 }
409 }
410 sum /= (j1-j0+1) * (i1-i0+1);
411 *dst++ = sum;
412 }
413 }
414 }
415 }
416 #endif
417
418
419 /*
420 * Return output image
421 */
422
423 if (packrowlength>0) {
424 rowlen = packrowlength;
425 }
426 else {
427 rowlen = widthout;
428 }
429 if (sizeout >= packalignment) {
430 rowstride = components * rowlen;
431 }
432 else {
433 rowstride = packalignment/sizeout
434 * CEILING( components * rowlen * sizeout, packalignment );
435 }
436
437 switch (typeout) {
438 case GL_UNSIGNED_BYTE:
439 k = 0;
440 for (i=0;i<heightout;i++) {
441 GLubyte *ubptr = (GLubyte *) dataout
442 + i * rowstride
443 + packskiprows * rowstride
444 + packskippixels * components;
445 for (j=0;j<widthout*components;j++) {
446 dummy(j, k+i);
447 *ubptr++ = (GLubyte) tempout[k++];
448 }
449 }
450 break;
451 case GL_BYTE:
452 k = 0;
453 for (i=0;i<heightout;i++) {
454 GLbyte *bptr = (GLbyte *) dataout
455 + i * rowstride
456 + packskiprows * rowstride
457 + packskippixels * components;
458 for (j=0;j<widthout*components;j++) {
459 dummy(j, k+i);
460 *bptr++ = (GLbyte) tempout[k++];
461 }
462 }
463 break;
464 case GL_UNSIGNED_SHORT:
465 k = 0;
466 for (i=0;i<heightout;i++) {
467 GLushort *usptr = (GLushort *) dataout
468 + i * rowstride
469 + packskiprows * rowstride
470 + packskippixels * components;
471 for (j=0;j<widthout*components;j++) {
472 dummy(j, k+i);
473 *usptr++ = (GLushort) tempout[k++];
474 }
475 }
476 break;
477 case GL_SHORT:
478 k = 0;
479 for (i=0;i<heightout;i++) {
480 GLshort *sptr = (GLshort *) dataout
481 + i * rowstride
482 + packskiprows * rowstride
483 + packskippixels * components;
484 for (j=0;j<widthout*components;j++) {
485 dummy(j, k+i);
486 *sptr++ = (GLshort) tempout[k++];
487 }
488 }
489 break;
490 case GL_UNSIGNED_INT:
491 k = 0;
492 for (i=0;i<heightout;i++) {
493 GLuint *uiptr = (GLuint *) dataout
494 + i * rowstride
495 + packskiprows * rowstride
496 + packskippixels * components;
497 for (j=0;j<widthout*components;j++) {
498 dummy(j, k+i);
499 *uiptr++ = (GLuint) tempout[k++];
500 }
501 }
502 break;
503 case GL_INT:
504 k = 0;
505 for (i=0;i<heightout;i++) {
506 GLint *iptr = (GLint *) dataout
507 + i * rowstride
508 + packskiprows * rowstride
509 + packskippixels * components;
510 for (j=0;j<widthout*components;j++) {
511 dummy(j, k+i);
512 *iptr++ = (GLint) tempout[k++];
513 }
514 }
515 break;
516 case GL_FLOAT:
517 k = 0;
518 for (i=0;i<heightout;i++) {
519 GLfloat *fptr = (GLfloat *) dataout
520 + i * rowstride
521 + packskiprows * rowstride
522 + packskippixels * components;
523 for (j=0;j<widthout*components;j++) {
524 dummy(j, k+i);
525 *fptr++ = tempout[k++];
526 }
527 }
528 break;
529 default:
530 return GLU_INVALID_ENUM;
531 }
532
533
534 /* free temporary image storage */
535 free( tempin );
536 free( tempout );
537
538 return 0;
539 }
540
541
542
543 /*
544 * Return the largest k such that 2^k <= n.
545 */
546 static GLint ilog2( GLint n )
547 {
548 GLint k;
549
550 if (n<=0) return 0;
551 for (k=0; n>>=1; k++) ;
552 return k;
553 }
554
555
556
557 /*
558 * Find the value nearest to n which is also a power of two.
559 */
560 static GLint round2( GLint n )
561 {
562 GLint m;
563
564 for (m=1; m<n; m*=2)
565 ;
566
567 /* m>=n */
568 if (m-n <= n-m/2) {
569 return m;
570 }
571 else {
572 return m/2;
573 }
574 }
575
576
577 /*
578 * Given an pixel format and datatype, return the number of bytes to
579 * store one pixel.
580 */
581 static GLint bytes_per_pixel( GLenum format, GLenum type )
582 {
583 GLint n, m;
584
585 switch (format) {
586 case GL_COLOR_INDEX:
587 case GL_STENCIL_INDEX:
588 case GL_DEPTH_COMPONENT:
589 case GL_RED:
590 case GL_GREEN:
591 case GL_BLUE:
592 case GL_ALPHA:
593 case GL_LUMINANCE:
594 n = 1;
595 break;
596 case GL_LUMINANCE_ALPHA:
597 n = 2;
598 break;
599 case GL_RGB:
600 n = 3;
601 break;
602 case GL_RGBA:
603 #ifdef GL_EXT_abgr
604 case GL_ABGR_EXT:
605 #endif
606 n = 4;
607 break;
608 default:
609 n = 0;
610 }
611
612 switch (type) {
613 case GL_UNSIGNED_BYTE: m = sizeof(GLubyte); break;
614 case GL_BYTE: m = sizeof(GLbyte); break;
615 case GL_BITMAP: m = 1; break;
616 case GL_UNSIGNED_SHORT: m = sizeof(GLushort); break;
617 case GL_SHORT: m = sizeof(GLshort); break;
618 case GL_UNSIGNED_INT: m = sizeof(GLuint); break;
619 case GL_INT: m = sizeof(GLint); break;
620 case GL_FLOAT: m = sizeof(GLfloat); break;
621 default: m = 0;
622 }
623
624 return n * m;
625 }
626
627
628
629 /*
630 * WARNING: This function isn't finished and has never been tested!!!!
631 */
632 GLint GLAPIENTRY gluBuild1DMipmaps( GLenum target, GLint components,
633 GLint width, GLenum format,
634 GLenum type, const void *data )
635 {
636 GLubyte *texture;
637 GLint levels, max_levels;
638 GLint new_width, max_width;
639 GLint i, j, k, l;
640
641 if (width < 1)
642 return GLU_INVALID_VALUE;
643
644 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &max_width );
645 max_levels = ilog2( max_width ) + 1;
646
647 /* Compute how many mipmap images to make */
648 levels = ilog2( width ) + 1;
649 if (levels>max_levels) {
650 levels = max_levels;
651 }
652
653 new_width = 1 << (levels-1);
654
655 texture = (GLubyte *) malloc( new_width * components );
656 if (!texture) {
657 return GLU_OUT_OF_MEMORY;
658 }
659
660 if (width != new_width) {
661 /* initial rescaling */
662 switch (type) {
663 case GL_UNSIGNED_BYTE:
664 {
665 GLubyte *ub_data = (GLubyte *) data;
666 for (i=0;i<new_width;i++) {
667 j = i * width / new_width;
668 for (k=0;k<components;k++) {
669 texture[i*components+k] = ub_data[j*components+k];
670 }
671 }
672 }
673 break;
674 default:
675 /* Not implemented */
676 return GLU_ERROR;
677 }
678 }
679
680 /* generate and load mipmap images */
681 for (l=0;l<levels;l++) {
682 glTexImage1D( GL_TEXTURE_1D, l, components, new_width, 0,
683 format, GL_UNSIGNED_BYTE, texture );
684
685 /* Scale image down to 1/2 size */
686 new_width = new_width / 2;
687 for (i=0;i<new_width;i++) {
688 for (k=0;k<components;k++) {
689 GLint sample1, sample2;
690 sample1 = (GLint) texture[i*2*components+k];
691 sample2 = (GLint) texture[(i*2+1)*components+k];
692 texture[i*components+k] = (GLubyte) ((sample1 + sample2) / 2);
693 }
694 }
695 }
696
697 free( texture );
698
699 /* make sure remaining mipmap levels are removed */
700 for (l=levels;l<max_levels;l++) {
701 glTexImage1D( GL_TEXTURE_1D, l, components, 0, 0,
702 format, GL_UNSIGNED_BYTE, NULL );
703 }
704
705 return 0;
706 }
707
708
709
710 GLint GLAPIENTRY gluBuild2DMipmaps( GLenum target, GLint components,
711 GLint width, GLint height, GLenum format,
712 GLenum type, const void *data )
713 {
714 GLint w, h, maxsize;
715 void *image, *newimage;
716 GLint neww, newh, level, bpp;
717 int error;
718
719 if (width < 1 || height < 1)
720 return GLU_INVALID_VALUE;
721
722 glGetIntegerv( GL_MAX_TEXTURE_SIZE, &maxsize );
723
724 w = round2( width );
725 if (w>maxsize) {
726 w = maxsize;
727 }
728 h = round2( height );
729 if (h>maxsize) {
730 h = maxsize;
731 }
732
733 bpp = bytes_per_pixel( format, type );
734 if (bpp==0) {
735 /* probably a bad format or type enum */
736 return GLU_INVALID_ENUM;
737 }
738
739 if (w!=width || h!=height) {
740 /* must rescale image to get "top" mipmap texture image */
741 image = malloc( (w+4) * h * bpp );
742 if (!image) {
743 return GLU_OUT_OF_MEMORY;
744 }
745 error = gluScaleImage( format, width, height, type, data,
746 w, h, type, image );
747 if (error) {
748 return error;
749 }
750 }
751 else {
752 image = (void *) data;
753 }
754
755 level = 0;
756 while (1) {
757 glTexImage2D( target, level, components, w, h, 0, format, type, image );
758
759 if (w==1 && h==1) break;
760
761 neww = (w<2) ? 1 : w/2;
762 newh = (h<2) ? 1 : h/2;
763 newimage = malloc( (neww+4) * newh * bpp );
764 if (!newimage) {
765 return GLU_OUT_OF_MEMORY;
766 }
767
768 error = gluScaleImage( format, w, h, type, image,
769 neww, newh, type, newimage );
770 if (error) {
771 return error;
772 }
773
774 if (image!=data) {
775 free( image );
776 }
777 image = newimage;
778
779 w = neww;
780 h = newh;
781 level++;
782 }
783
784 if (image!=data) {
785 free( image );
786 }
787
788 return 0;
789 }
790