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