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