1 /*
2 * GRacer
3 *
4 * Copyright (C) 1999 Takashi Matsuda <matsu@users.sourceforge.net>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 * USA
20 */
21
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include "gr_global.h"
26 #include "gr_memory.h"
27 #include "gr_texture.h"
28 #include "gr_debug.h"
29 #include <config.h>
30 #include <GL/gl.h>
31 #include <GL/glu.h>
32 #include <gif_lib.h>
33 #include <jpeglib.h>
34 #include <jerror.h>
35 #include <png.h>
36
37 #ifdef ENABLE_GIF
38 static int gif_decode (GifFileType *file, GifRecordType find);
39 static int read_gif_file (GrTexture *texture, char *filename);
40 #endif
41
42 #ifdef ENABLE_JPEG
43 static int read_jpeg_file (GrTexture *texture, char *filename);
44 #endif
45
46 #ifdef ENABLE_PNG
47 static int read_png_file (GrTexture *texture, char *filename);
48 #endif
49
50
51 static int texture_is_active = 1;
52
53 typedef struct _GrTextureImport GrTextureImport;
54
55 struct _GrTextureImport {
56 const char *ext;
57 int length;
58 int (*func) (GrTexture *, char *);
59 };
60
61 static GrTextureImport texture_import[] = {
62 #ifdef ENABLE_GIF
63 {".gif", 4, read_gif_file},
64 #endif
65 #ifdef ENABLE_JPEG
66 {".jpg", 4, read_jpeg_file},
67 #endif
68 #ifdef ENABLE_PNG
69 {".png", 4, read_png_file},
70 #endif
71 {NULL, 0, NULL},
72 };
73
74 static void
gr_texture_free(GrTexture * texture)75 gr_texture_free (GrTexture *texture)
76 {
77 free (texture->texel);
78 free (texture);
79 }
80
81 GrTexture *
gr_texture_new(void)82 gr_texture_new (void) {
83 GrTexture *texture;
84
85 texture = gr_new0 (GrTexture, 1);
86 gr_FREE_FUNC (texture, gr_texture_free);
87
88 return texture;
89 }
90
91 GrTexture *
gr_texture_new_from_file(char * filename)92 gr_texture_new_from_file (char *filename) {
93 GrTexture *texture;
94 int success = 0;
95 int length;
96 GrTextureImport *import;
97 int i;
98
99 if (filename == NULL)
100 return NULL;
101
102 texture = gr_texture_new ();
103
104 length = strlen (filename);
105 for (i=0; texture_import[i].ext != NULL; i++) {
106 import = &texture_import[i];
107 if (strcasecmp (filename + (length - import->length), import->ext) == 0) {
108 if ((*import->func)(texture, filename) == 0)
109 success = 1;
110 break;
111 }
112 }
113 if (!success) {
114 gr_DECREF (texture);
115 return NULL;
116 }
117
118 return texture;
119 }
120
121 void
gr_texture_set_active(int state)122 gr_texture_set_active (int state)
123 {
124 if (state) {
125 texture_is_active = 1;
126 } else {
127 texture_is_active = 0;
128 }
129 }
130
131 int
gr_texture_get_active(void)132 gr_texture_get_active (void)
133 {
134 return texture_is_active;
135 }
136
137 void
gr_texture_setup_gl(GrTexture * texture,GrObjectDrawOption option)138 gr_texture_setup_gl (GrTexture *texture, GrObjectDrawOption option)
139 {
140 if (texture->name == 0) {
141 GL_CHECK(glGenTextures (1, &(texture->name)));
142 GL_CHECK(glBindTexture (GL_TEXTURE_2D, texture->name));
143 GL_CHECK(glPixelStorei (GL_UNPACK_ALIGNMENT, 1));
144 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
145 GL_REPEAT));
146 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
147 GL_REPEAT));
148
149 if (option & GR_OBJECT_MAG_LINEAR) {
150 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
151 GL_LINEAR));
152 } else {
153 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
154 GL_NEAREST));
155 }
156
157 if (option & (GR_OBJECT_MIPMAP)) {
158 switch (option & (GR_OBJECT_MIN_LINEAR | GR_OBJECT_MIP_LINEAR)) {
159 default:
160 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
161 GL_NEAREST_MIPMAP_NEAREST));
162 break;
163
164 case GR_OBJECT_MIP_LINEAR:
165 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
166 GL_NEAREST_MIPMAP_LINEAR));
167 break;
168
169 case GR_OBJECT_MIN_LINEAR:
170 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
171 GL_LINEAR_MIPMAP_NEAREST));
172 break;
173
174 case GR_OBJECT_MIN_LINEAR | GR_OBJECT_MIP_LINEAR:
175 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
176 GL_LINEAR_MIPMAP_LINEAR));
177 break;
178 }
179 } else {
180 if (option & GR_OBJECT_MIN_LINEAR) {
181 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
182 GL_LINEAR));
183 } else {
184 GL_CHECK(glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
185 GL_NEAREST));
186 }
187 }
188
189 if (option & GR_OBJECT_MIPMAP) {
190 GL_CHECK(gluBuild2DMipmaps (GL_TEXTURE_2D,
191 texture->format,
192 texture->width,
193 texture->height,
194 texture->format,
195 GL_UNSIGNED_BYTE,
196 texture->texel));
197 } else {
198 GL_CHECK(glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA,
199 texture->width, texture->height,
200 0, texture->format,
201 GL_UNSIGNED_BYTE, texture->texel));
202 }
203 }
204 }
205
206 void
gr_texture_release_gl(GrTexture * texture)207 gr_texture_release_gl (GrTexture *texture)
208 {
209 if (texture->name) {
210 glDeleteTextures (1, &texture->name);
211 }
212 texture->name = 0;
213 }
214
215 void
gr_texture_bind(GrTexture * texture)216 gr_texture_bind (GrTexture *texture)
217 {
218 GL_CHECK(glBindTexture (GL_TEXTURE_2D, texture->name));
219 }
220
221 #ifdef ENABLE_GIF
222 static int
gif_decode(GifFileType * file,GifRecordType find)223 gif_decode (GifFileType *file, GifRecordType find)
224 {
225 int code;
226 GifRecordType type;
227 GifByteType *ext;
228
229 while (1) {
230 if (DGifGetRecordType (file, &type) == GIF_ERROR) {
231 gr_warning ("can't get record type");
232 return -1;
233 }
234
235 switch (type) {
236 case UNDEFINED_RECORD_TYPE:
237 break;
238
239 case SCREEN_DESC_RECORD_TYPE:
240 DGifGetScreenDesc (file);
241 break;
242
243 case IMAGE_DESC_RECORD_TYPE:
244 DGifGetImageDesc (file);
245 break;
246
247 case EXTENSION_RECORD_TYPE:
248 if (find == EXTENSION_RECORD_TYPE)
249 return 1;
250
251 DGifGetExtension (file, &code, &ext);
252
253 while (ext != NULL) {
254 DGifGetExtensionNext (file, &ext);
255 }
256
257 break;
258
259 case TERMINATE_RECORD_TYPE:
260 return 0;
261 }
262
263 if (find == type)
264 return 1;
265 }
266 }
267
268 static int
read_gif_file(GrTexture * texture,char * filename)269 read_gif_file (GrTexture *texture, char *filename)
270 {
271 GifFileType *file;
272 GifPixelType *line_buf;
273 int i, j;
274 int index;
275 int width, height;
276
277 #if GIFLIB_MAJOR >= 5
278 file = DGifOpenFileName (filename, NULL);
279 #else
280 file = DGifOpenFileName (filename);
281 #endif
282 if (!file)
283 return -1;
284
285 width = texture->width = file->SWidth;
286 height = texture->height = file->SHeight;
287 texture->texel = (GLubyte *) gr_new0 (GLubyte, width * height * 4);
288
289 line_buf = gr_new (GifPixelType, width);
290
291 if (gif_decode (file, IMAGE_DESC_RECORD_TYPE) != 1) {
292 gr_warning ("can not fine screen desc in gif image file.");
293
294 free (line_buf);
295 return -1;
296 }
297
298 index = file->Image.Top * width + file->Image.Left;
299 for (i=0; i<file->Image.Height; i++) {
300 DGifGetLine (file, line_buf, width);
301 for (j=0; j<file->Image.Width; j++) {
302 GLubyte *texel = texture->texel + (index + i * width + j) * 4;
303 if (line_buf[j] == file->SBackGroundColor) {
304 texel[3] = 0;
305 } else {
306 texel[3] = 255;
307 }
308 if (file->SColorMap) {
309 texel[0] = file->SColorMap->Colors[line_buf[j]].Red;
310 texel[1] = file->SColorMap->Colors[line_buf[j]].Green;
311 texel[2] = file->SColorMap->Colors[line_buf[j]].Blue;
312 } else {
313 texel[0] = texel[1] = texel[2] = line_buf[j];
314 }
315 }
316 }
317
318 texture->format = GL_RGBA;
319
320 free (line_buf);
321 return 0;
322 }
323 #endif /* ENABLE_GIF */
324
325 #ifdef ENABLE_JPEG
326 static int
read_jpeg_file(GrTexture * texture,char * filename)327 read_jpeg_file (GrTexture *texture, char *filename)
328 {
329 FILE *file;
330 struct jpeg_decompress_struct cinfo;
331 struct jpeg_error_mgr jerr;
332 JSAMPARRAY buffer;
333 unsigned char *data = NULL;
334 int width;
335 int height;
336 int pixelsize;
337 int line_length;
338 int res;
339 int i;
340
341 cinfo.err = jpeg_std_error (&jerr);
342 jpeg_create_decompress (&cinfo);
343 file = fopen (filename, "r");
344 if (!file) {
345 return -1;
346 }
347 jpeg_stdio_src (&cinfo, file);
348 res = jpeg_read_header (&cinfo, 1);
349 if (res != JPEG_HEADER_OK) {
350 goto ERROR;
351 }
352 jpeg_start_decompress (&cinfo);
353 if (cinfo.data_precision != 8) {
354 goto ERROR;
355 }
356
357 texture->width = width = cinfo.output_width;
358 texture->height = height = cinfo.output_height;
359 pixelsize = cinfo.output_components;
360 line_length = width * pixelsize;
361
362 switch (cinfo.out_color_space) {
363 case JCS_GRAYSCALE:
364 texture->format = GL_LUMINANCE;
365 break;
366
367 case JCS_RGB:
368 texture->format = GL_RGB;
369 break;
370
371 default:
372 goto ERROR;
373 }
374
375 texture->texel = data = gr_new (unsigned char, line_length * height);
376 if (!data) {
377 jpeg_destroy_decompress (&cinfo);
378 return -1;
379 }
380 buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo, JPOOL_IMAGE,
381 line_length, 1);
382
383 for (i=0; i<height; i++) {
384 jpeg_read_scanlines (&cinfo, buffer, 1);
385 memcpy (data + line_length * i, buffer[0], line_length);
386 }
387
388 jpeg_destroy_decompress (&cinfo);
389
390 return 0;
391
392 ERROR:
393 if (data) {
394 free (data);
395 }
396 jpeg_destroy_decompress (&cinfo);
397 return -1;
398 }
399 #endif /* ENABLE_JPEG */
400
401 #ifdef ENABLE_PNG
402 #define PNG_BYTES_TO_CHECK 4
403
404 static int
read_png_file(GrTexture * texture,char * filename)405 read_png_file (GrTexture *texture, char *filename)
406 {
407 FILE *file;
408 png_uint_32 width;
409 png_uint_32 height;
410 int i;
411 char header[PNG_BYTES_TO_CHECK];
412 png_structp png_ptr = NULL;
413 png_infop info_ptr = NULL;
414 int bit_depth, color_type, interlace_type;
415 png_bytep *row_pointers = NULL;
416 GLubyte *buf = NULL;
417 int rowbytes;
418
419 file = fopen (filename, "r");
420 if (!file) {
421 goto ERROR;
422 }
423 if (fread (header, 1, PNG_BYTES_TO_CHECK, file) != PNG_BYTES_TO_CHECK) {
424 goto ERROR;
425 }
426 if (png_sig_cmp (header, 0, PNG_BYTES_TO_CHECK)) {
427 goto ERROR;
428 }
429
430 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
431 if (!png_ptr) {
432 goto ERROR;
433 }
434 info_ptr = png_create_info_struct (png_ptr);
435 if (!info_ptr) {
436 goto ERROR;
437 }
438 if (setjmp (png_jmpbuf(png_ptr))) {
439 goto ERROR;
440 }
441 png_init_io (png_ptr, file);
442 png_set_sig_bytes (png_ptr, PNG_BYTES_TO_CHECK);
443
444 png_read_info (png_ptr, info_ptr);
445 png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
446 &interlace_type, NULL, NULL);
447
448 /* string 16bit/color files down to 8bits/color */
449 png_set_strip_16 (png_ptr);
450
451 /* extract multiple pixels with bit depth 1, 2, and 4 from signle byte into
452 * separate bytes. */
453 png_set_packing (png_ptr);
454
455 /* expand paletted colors into true RGB triplets */
456 if (color_type == PNG_COLOR_TYPE_PALETTE)
457 png_set_expand (png_ptr);
458
459 /* expand grayscale images to the full 8 bits from 1, 2, or 4 bits/pixels */
460 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
461 png_set_expand (png_ptr);
462
463 /* expand paletted or RGB images with transparency to full alpha channels
464 * so the data will be available as RGBA quartets */
465 if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
466 png_set_expand (png_ptr);
467
468 rowbytes = png_get_rowbytes (png_ptr, info_ptr);
469 buf = gr_new (GLubyte, rowbytes * height);
470 row_pointers = gr_new (png_bytep, height);
471 for (i=0; i<height; i++) {
472 row_pointers[height - i - 1] = buf + rowbytes * i;
473 }
474
475 png_read_image (png_ptr, row_pointers);
476 png_read_end (png_ptr, info_ptr);
477
478 switch (png_get_color_type (png_ptr, info_ptr)) {
479 case PNG_COLOR_TYPE_GRAY:
480 texture->format = GL_LUMINANCE;
481 break;
482
483 case PNG_COLOR_TYPE_GRAY_ALPHA:
484 texture->format = GL_LUMINANCE_ALPHA;
485 break;
486
487 case PNG_COLOR_TYPE_RGB:
488 texture->format = GL_RGB;
489 break;
490
491 case PNG_COLOR_TYPE_RGB_ALPHA:
492 texture->format = GL_RGBA;
493 break;
494
495 default:
496 goto ERROR;
497 }
498
499 texture->texel = buf;
500 texture->width = width;
501 texture->height = height;
502
503 png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp) NULL);
504 free (row_pointers);
505
506 fclose (file);
507
508 return 0;
509
510 ERROR:
511 if (file) {
512 fclose (file);
513 }
514 if (png_ptr && info_ptr) {
515 png_destroy_info_struct (png_ptr, NULL);
516 }
517
518 if (png_ptr) {
519 png_destroy_read_struct (&png_ptr, NULL, NULL);
520 }
521 free (buf);
522 free (row_pointers);
523
524 return -1;
525 }
526
527 #endif /* ENABLE_PNG */
528