1 /*
2 	Copyright (C) 2005 by Ruben Henner Zilibowitz <rzilibowitz@users.sourceforge.net>
3 	Part of the Toy Cars Project http://toycars.sourceforge.net
4 
5 	This program is free software; you can redistribute it and/or modify
6 	it under the terms of the license.
7 	This program is distributed in the hope that it will be useful,
8 	but WITHOUT ANY WARRANTY.
9 
10 	See the COPYING file for more details.
11 */
12 
13 /*
14  *  ScImage.cpp
15  *  Scoobie
16  *
17  *  Created by Ruben Henner Zilibowitz on 26/11/04.
18  *
19  */
20 
21 #include "ScImage.h"
22 #include "ScException.h"
23 #include <cstdio>
24 #include <cstring>
25 
26 // constructor - Loads image into texture memory. Fits powers of two textures to the
27 // size of the image. If dimensions are odd then one pixel will be cropped.
28 //
29 // border must be either 0 or 1. Usually 1 is desireable since then a one pixel border of the
30 // image is not drawn but instead used to do linear filtering. If 0 border is used then
31 // image is drawn at original dimensions and the border color option is used by OpenGL.
32 //
33 // flip can be true or false. If it is true, the pixel data will be flipped vertically when
34 // stored as a texture. This can be usefull because Scoobie's coordinate system is upside down.
35 //
ScImage(short border,const ScPixelData & pixelData,bool flip)36 ScImage::ScImage(short border, const ScPixelData& pixelData, bool flip)
37 {
38 	GLsizei maxtexsize;
39 	short col, row, k;
40 	short x, y;
41 	long offset;
42 	int imageWidth = pixelData.width;
43 	int imageHeight = pixelData.height;
44 	const char* pixels = pixelData.pixels;
45 
46 	// make width and height even
47 	if (imageWidth % 2 == 1)	imageWidth--;
48 	if (imageHeight % 2 == 1)	imageHeight--;
49 
50 	// get the maximum texture size
51 	glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxtexsize);
52 
53 	// calculate number of columns and rows
54 	columns = calculateNumCells(maxtexsize, imageWidth);
55 	rows = calculateNumCells(maxtexsize, imageHeight);
56 
57 	// generate texture names
58 	texs = new GLuint [columns*rows];
59 	glGenTextures(columns*rows, texs);
60 
61 	// allocate column info arrays
62 	colSize = new short [columns];
63 	colTC1 = new ScFloat [columns];
64 	colTC2 = new ScFloat [columns];
65 
66 	// allocate row info  arrays
67 	rowSize = new short [rows];
68 	rowTC1 = new ScFloat [rows];
69 	rowTC2 = new ScFloat [rows];
70 
71 	// calculate cell sizes and texture coordinates
72 	calculateCellSizeAndTC(maxtexsize, imageWidth, colSize, colTC1, colTC2);
73 	calculateCellSizeAndTC(maxtexsize, imageHeight, rowSize, rowTC1, rowTC2);
74 
75 	// make sure 2d textures are enabled
76 	glEnable(GL_TEXTURE_2D);
77 
78 	// tell OpenGL what the row length of the image data is
79 	glPixelStorei(GL_UNPACK_ROW_LENGTH, pixelData.pitch / pixelData.bytes_pp);
80 
81 	// flip pixels vertically if necessary
82 	if (flip)
83 	{
84 		char* flippedPix = new char [pixelData.pitch * pixelData.height];
85 		if (flippedPix == NULL)
86 			ScThrowErr("out of memory whilst flipping pixels");
87 
88 		for (y = 0; y < pixelData.height; y++)
89 		{
90 			// copy a row
91 			memcpy(	flippedPix + y*pixelData.pitch,
92 					pixelData.pixels + (pixelData.height-y-1)*pixelData.pitch,
93 					pixelData.pitch);
94 		}
95 
96 		pixels = flippedPix;
97 	}
98 
99 	// upload textures
100 
101 	// init texture counter
102 	k = 0;
103 
104 	// init y location
105 	y = 0;
106 
107 	// loop through rows
108 	for (row = 0; row < rows; row++)
109 	{
110 		// init x location
111 		x = 0;
112 
113 		// loop through columns
114 		for (col = 0; col < columns; col++)
115 		{
116 			// bind to correct texture id
117 			glBindTexture(GL_TEXTURE_2D, texs[k]);
118 
119 			// set texture parameters
120 			if (border > 0)
121 			{
122 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
123 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
124 			}
125 			else
126 			{
127 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
128 				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
129 			}
130 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
131 			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
132 
133 			// calculate offset in pixel data
134 			offset = y*pixelData.pitch + x*pixelData.bytes_pp;
135 
136 			// upload texture to graphics card
137 			glTexImage2D(	GL_TEXTURE_2D,
138 							0,
139 							pixelData.internalformat,
140 							colSize[col]+2,
141 							rowSize[row]+2,
142 							0,
143 							pixelData.format,
144 							pixelData.type,
145 							pixels + offset);
146 			if (glGetError() != GL_NO_ERROR) {
147 				char msg[256];
148 				sprintf(msg, "OpenGL error number %d", glGetError());
149 				ScThrowErr(msg);
150 			}
151 
152 			// move x location
153 			x += colSize[col];
154 
155 			// increment texture counter
156 			k++;
157 		}
158 
159 		// move y location
160 		y += rowSize[row];
161 	}
162 
163 	// Above calculation assume a border of 1 pixel in the image.
164 	// If there is no border modify row and column info. OpenGL
165 	// will use the border color setting to draw scaled image.
166 	// Image width and height are each made two pixels larger.
167 	if (border == 0)
168 	{
169 		colTC1[0] = 0.0;
170 		colTC2[columns-1] = 1.0;
171 		colSize[0]++;
172 		colSize[columns-1]++;
173 
174 		rowTC1[0] = 0.0;
175 		rowTC2[rows-1] = 1.0;
176 		rowSize[0]++;
177 		rowSize[rows-1]++;
178 	}
179 
180 	// if flipping was used we must deallocate the memory for the flipped pixels
181 	if (flip)
182 		delete [] pixels;
183 }
184 
185 //
186 // destructor - frees up our memory
187 //
~ScImage()188 ScImage::~ScImage()
189 {
190 	// free textures
191 	if (columns*rows > 0)
192 		glDeleteTextures(columns*rows, texs);
193 
194 	// free array memory
195 	if (texs)		delete [] texs;
196 	if (colSize)	delete [] colSize;
197 	if (colTC1)		delete [] colTC1;
198 	if (colTC2)		delete [] colTC2;
199 	if (rowSize)	delete [] rowSize;
200 	if (rowTC1)		delete [] rowTC1;
201 	if (rowTC2)		delete [] rowTC2;
202 }
203 
204 //
205 // draw - draws entire image at given location
206 //
draw(ScFloat locx,ScFloat locy)207 void ScImage::draw(ScFloat locx, ScFloat locy)
208 {
209 	short col, row, k;
210 	ScFloat x1, y1, x2, y2;
211 	ScFloat tx1, ty1, tx2, ty2;
212 
213 	// init texture counter
214 	k = 0;
215 
216 	// init pixel location y1
217 	y1 = locy;
218 
219 	// loop through rows
220 	for (row = 0; row < rows; row++)
221 	{
222 		// get texture coordinates
223 		ty1 = rowTC1[row];
224 		ty2 = rowTC2[row];
225 
226 		// calculate pixel location y2
227 		y2 = y1 + rowSize[row];
228 
229 		// init pixel location x1
230 		x1 = locx;
231 
232 		// loop through columns
233 		for (col = 0; col < columns; col++)
234 		{
235 			// calculate pixel location x2
236 			x2 = x1 + colSize[col];
237 
238 			// get texture coordinates
239 			tx1 = colTC1[col];
240 			tx2 = colTC2[col];
241 
242 			// bind to correct texture id
243 			glBindTexture(GL_TEXTURE_2D, texs[k]);
244 
245 			// draw texture
246 			glBegin(GL_QUADS);
247 			glTexCoord2d(tx1, ty1);	glVertex2d(x1, y1);
248 			glTexCoord2d(tx2, ty1);	glVertex2d(x2, y1);
249 			glTexCoord2d(tx2, ty2);	glVertex2d(x2, y2);
250 			glTexCoord2d(tx1, ty2);	glVertex2d(x1, y2);
251 			glEnd();
252 
253 			// move pixel location x1 to x2
254 			x1 = x2;
255 
256 			// increment texture counter
257 			k++;
258 		}
259 
260 		// move pixel location y1 to y2
261 		y1 = y2;
262 	}
263 }
264 
265 // given a maximum texture size and a distance, calculate how many
266 // textures are needed to span the distance where each texture is a
267 // power of two and adjacent textures overlap by two pixels.
calculateNumCells(short maxtexsize,short distance)268 short ScImage::calculateNumCells(short maxtexsize, short distance)
269 {
270 	short cellCount = 0;
271 
272 	// adjust distance for border on each side
273 	distance -= 2;
274 
275 	do
276 	{
277 		// count number of cells with current size will fit
278 		while (distance >= maxtexsize - 2)
279 		{
280 			distance -= maxtexsize - 2;
281 			cellCount++;
282 		}
283 
284 		// drop to smaller texture size
285 		maxtexsize /= 2;
286 	}
287 	while (maxtexsize > 2);
288 
289 	// if distance still remains then textures got too small
290 	if (distance > 0)
291 		printf("Warning: Texture size too small. Distance remaining: %d\n", distance);
292 
293 	// return the count
294 	return cellCount;
295 }
296 
297 // calculate the sizes and texture coordinates for each cell
298 // given the maximum texture size and the distance.
calculateCellSizeAndTC(short maxtexsize,short distance,short * sizes,ScFloat * tc1,ScFloat * tc2)299 void ScImage::calculateCellSizeAndTC(short maxtexsize, short distance, short* sizes, ScFloat* tc1, ScFloat* tc2)
300 {
301 	short cellCount = 0;
302 
303 	// adjust distance for border on each side
304 	distance -= 2;
305 
306 	do
307 	{
308 		// count number of cells with current size will fit
309 		while (distance >= maxtexsize - 2)
310 		{
311 			// calculate cell info
312 			sizes[cellCount] = maxtexsize - 2;
313 			tc1[cellCount] = 1.0 / ScFloat(maxtexsize);
314 			tc2[cellCount] = 1.0 - tc1[cellCount];
315 
316 			// decrease distance
317 			distance -= maxtexsize - 2;
318 
319 			// count a cell
320 			cellCount++;
321 		}
322 
323 		// drop to smaller texture size
324 		maxtexsize /= 2;
325 	}
326 	while (maxtexsize > 2);
327 }
328