1 /*
2  * Copyright (c) 2002-2008 LWJGL Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'LWJGL' nor the names of
17  *   its contributors may be used to endorse or promote products derived
18  *   from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 package org.lwjgl.util.glu;
33 
34 import java.nio.ByteBuffer;
35 
36 import org.lwjgl.BufferUtils;
37 
38 import static org.lwjgl.opengl.GL11.*;
39 import static org.lwjgl.util.glu.GLU.*;
40 
41 /**
42  * MipMap.java
43  *
44  *
45  * Created 11-jan-2004
46  * @author Erik Duijs
47  */
48 public class MipMap extends Util {
49 
50 	/**
51 	 * Method gluBuild2DMipmaps
52 	 *
53 	 * @param target
54 	 * @param components
55 	 * @param width
56 	 * @param height
57 	 * @param format
58 	 * @param type
59 	 * @param data
60 	 * @return int
61 	 */
gluBuild2DMipmaps(final int target, final int components, final int width, final int height, final int format, final int type, final ByteBuffer data)62 	public static int gluBuild2DMipmaps(final int target,
63 	                                    final int components, final int width, final int height,
64 	                                    final int format, final int type, final ByteBuffer data) {
65 		if ( width < 1 || height < 1 ) return GLU_INVALID_VALUE;
66 
67 		final int bpp = bytesPerPixel(format, type);
68 		if ( bpp == 0 )
69 			return GLU_INVALID_ENUM;
70 
71 		final int maxSize = glGetIntegerv(GL_MAX_TEXTURE_SIZE);
72 
73 		int w = nearestPower(width);
74 		if ( w > maxSize )
75 			w = maxSize;
76 
77 		int h = nearestPower(height);
78 		if ( h > maxSize )
79 			h = maxSize;
80 
81 		// Get current glPixelStore state
82 		PixelStoreState pss = new PixelStoreState();
83 
84 		// set pixel packing
85 		glPixelStorei(GL_PACK_ROW_LENGTH, 0);
86 		glPixelStorei(GL_PACK_ALIGNMENT, 1);
87 		glPixelStorei(GL_PACK_SKIP_ROWS, 0);
88 		glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
89 
90 		ByteBuffer image;
91 		int retVal = 0;
92 		boolean done = false;
93 
94 		if ( w != width || h != height ) {
95 			// must rescale image to get "top" mipmap texture image
96 			image = BufferUtils.createByteBuffer((w + 4) * h * bpp);
97 			int error = gluScaleImage(format, width, height, type, data, w, h, type, image);
98 			if ( error != 0 ) {
99 				retVal = error;
100 				done = true;
101 			}
102 
103 			/* set pixel unpacking */
104 			glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
105 			glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
106 			glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
107 			glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
108 		} else {
109 			image = data;
110 		}
111 
112 		ByteBuffer bufferA = null;
113 		ByteBuffer bufferB = null;
114 
115 		int level = 0;
116 		while ( !done ) {
117 			if (image != data) {
118 				/* set pixel unpacking */
119 				glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
120 				glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
121 				glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
122 				glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
123 			}
124 
125 			glTexImage2D(target, level, components, w, h, 0, format, type, image);
126 
127 			if ( w == 1 && h == 1 )
128 				break;
129 
130 			final int newW = (w < 2) ? 1 : w >> 1;
131 			final int newH = (h < 2) ? 1 : h >> 1;
132 
133 			final ByteBuffer newImage;
134 
135 			if ( bufferA == null )
136 				newImage = (bufferA = BufferUtils.createByteBuffer((newW + 4) * newH * bpp));
137 			else if ( bufferB == null )
138 				newImage = (bufferB = BufferUtils.createByteBuffer((newW + 4) * newH * bpp));
139 			else
140 				newImage = bufferB;
141 
142 			int error = gluScaleImage(format, w, h, type, image, newW, newH, type, newImage);
143 			if ( error != 0 ) {
144 				retVal = error;
145 				done = true;
146 			}
147 
148 			image = newImage;
149 			if ( bufferB != null )
150 				bufferB = bufferA;
151 
152 			w = newW;
153 			h = newH;
154 			level++;
155 		}
156 
157 		// Restore original glPixelStore state
158 		pss.save();
159 
160 		return retVal;
161 	}
162 
163 	/**
164 	 * Method gluScaleImage.
165 	 * @param format
166 	 * @param widthIn
167 	 * @param heightIn
168 	 * @param typein
169 	 * @param dataIn
170 	 * @param widthOut
171 	 * @param heightOut
172 	 * @param typeOut
173 	 * @param dataOut
174 	 * @return int
175 	 */
gluScaleImage(int format, int widthIn, int heightIn, int typein, ByteBuffer dataIn, int widthOut, int heightOut, int typeOut, ByteBuffer dataOut)176 	public static int gluScaleImage(int format,
177 	                                int widthIn, int heightIn, int typein, ByteBuffer dataIn,
178 	                                int widthOut, int heightOut, int typeOut, ByteBuffer dataOut) {
179 
180 		final int components = compPerPix(format);
181 		if ( components == -1 )
182 			return GLU_INVALID_ENUM;
183 
184 		int i, j, k;
185 		float[] tempIn, tempOut;
186 		float sx, sy;
187 		int sizein, sizeout;
188 		int rowstride, rowlen;
189 
190 		// temp image data
191 		tempIn = new float[widthIn * heightIn * components];
192 		tempOut = new float[widthOut * heightOut * components];
193 
194 		// Determine bytes per input type
195 		switch ( typein ) {
196 			case GL_UNSIGNED_BYTE:
197 				sizein = 1;
198 				break;
199 			case GL_FLOAT:
200 				sizein = 4;
201 				break;
202 			default:
203 				return GL_INVALID_ENUM;
204 		}
205 
206 		// Determine bytes per output type
207 		switch ( typeOut ) {
208 			case GL_UNSIGNED_BYTE:
209 				sizeout = 1;
210 				break;
211 			case GL_FLOAT:
212 				sizeout = 4;
213 				break;
214 			default:
215 				return GL_INVALID_ENUM;
216 		}
217 
218 		// Get glPixelStore state
219 		PixelStoreState pss = new PixelStoreState();
220 
221 		//Unpack the pixel data and convert to floating point
222 		if ( pss.unpackRowLength > 0 )
223 			rowlen = pss.unpackRowLength;
224 		else
225 			rowlen = widthIn;
226 
227 		if ( sizein >= pss.unpackAlignment )
228 			rowstride = components * rowlen;
229 		else
230 			rowstride = pss.unpackAlignment / sizein * ceil(components * rowlen * sizein, pss.unpackAlignment);
231 
232 		switch ( typein ) {
233 			case GL_UNSIGNED_BYTE:
234 				k = 0;
235 				dataIn.rewind();
236 				for ( i = 0; i < heightIn; i++ ) {
237 					int ubptr = i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components;
238 					for ( j = 0; j < widthIn * components; j++ ) {
239 						tempIn[k++] = dataIn.get(ubptr++) & 0xff;
240 					}
241 				}
242 				break;
243 			case GL_FLOAT:
244 				k = 0;
245 				dataIn.rewind();
246 				for ( i = 0; i < heightIn; i++ )
247 				{
248 					int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components);
249 					for ( j = 0; j < widthIn * components; j++ )
250 					{
251 						tempIn[k++] = dataIn.getFloat(fptr);
252 						fptr += 4;
253 					}
254 				}
255 				break;
256 			default:
257 				return GLU_INVALID_ENUM;
258 		}
259 
260 		// Do scaling
261 		sx = (float)widthIn / (float)widthOut;
262 		sy = (float)heightIn / (float)heightOut;
263 
264 		float[] c = new float[components];
265 		int src, dst;
266 
267 		for ( int iy = 0; iy < heightOut; iy++ ) {
268 			for ( int ix = 0; ix < widthOut; ix++ ) {
269 				int x0 = (int)(ix * sx);
270 				int x1 = (int)((ix + 1) * sx);
271 				int y0 = (int)(iy * sy);
272 				int y1 = (int)((iy + 1) * sy);
273 
274 				int readPix = 0;
275 
276 				// reset weighted pixel
277 				for ( int ic = 0; ic < components; ic++ ) {
278 					c[ic] = 0;
279 				}
280 
281 				// create weighted pixel
282 				for ( int ix0 = x0; ix0 < x1; ix0++ ) {
283 					for ( int iy0 = y0; iy0 < y1; iy0++ ) {
284 
285 						src = (iy0 * widthIn + ix0) * components;
286 
287 						for ( int ic = 0; ic < components; ic++ ) {
288 							c[ic] += tempIn[src + ic];
289 						}
290 
291 						readPix++;
292 					}
293 				}
294 
295 				// store weighted pixel
296 				dst = (iy * widthOut + ix) * components;
297 
298 				if ( readPix == 0 ) {
299 					// Image is sized up, caused by non power of two texture as input
300 					src = (y0 * widthIn + x0) * components;
301 					for ( int ic = 0; ic < components; ic++ ) {
302 						tempOut[dst++] = tempIn[src + ic];
303 					}
304 				} else {
305 					// sized down
306 					for ( k = 0; k < components; k++ ) {
307 						tempOut[dst++] = c[k] / readPix;
308 					}
309 				}
310 			}
311 		}
312 
313 
314 		// Convert temp output
315 		if ( pss.packRowLength > 0 )
316 			rowlen = pss.packRowLength;
317 		else
318 			rowlen = widthOut;
319 
320 		if ( sizeout >= pss.packAlignment )
321 			rowstride = components * rowlen;
322 		else
323 			rowstride = pss.packAlignment / sizeout * ceil(components * rowlen * sizeout, pss.packAlignment);
324 
325 		switch ( typeOut ) {
326 			case GL_UNSIGNED_BYTE:
327 				k = 0;
328 				for ( i = 0; i < heightOut; i++ ) {
329 					int ubptr = i * rowstride + pss.packSkipRows * rowstride + pss.packSkipPixels * components;
330 
331 					for ( j = 0; j < widthOut * components; j++ ) {
332 						dataOut.put(ubptr++, (byte)tempOut[k++]);
333 					}
334 				}
335 				break;
336 			case GL_FLOAT:
337 				k = 0;
338 				for ( i = 0; i < heightOut; i++ ) {
339 					int fptr = 4 * (i * rowstride + pss.unpackSkipRows * rowstride + pss.unpackSkipPixels * components);
340 
341 					for ( j = 0; j < widthOut * components; j++ ) {
342 						dataOut.putFloat(fptr, tempOut[k++]);
343 						fptr += 4;
344 					}
345 				}
346 				break;
347 			default:
348 				return GLU_INVALID_ENUM;
349 		}
350 
351 		return 0;
352 	}
353 }
354