]> git.mxchange.org Git - simgear.git/blob - simgear/screen/texture.cxx
Update the code a bit more, add a function to retreive the last error string and...
[simgear.git] / simgear / screen / texture.cxx
1 /*
2  * Texture manipulation routines
3  *
4  * Copyright (c) Mark J. Kilgard, 1997.
5  * Code added in april 2003 by Erik Hofman
6  *
7  * This program is freely distributable without licensing fees 
8  * and is provided without guarantee or warrantee expressed or 
9  * implied. This program is -not- in the public domain.
10  *
11  * $Id$
12  */
13
14 #include <simgear/compiler.h>
15
16 #ifdef WIN32
17 # include <windows.h>
18 #endif
19
20 #include SG_GLU_H
21
22 #include <zlib.h>
23
24 #include "texture.hxx"
25 #include "colours.h"
26
27
28 const char *FILE_OPEN_ERROR = "Unable to open file.";
29 const char *WRONG_COUNT = "Unsupported number of color channels.";
30 const char *NO_TEXTURE = "No texture data resident.";
31 const char *OUT_OF_MEMORY = "Out of memory.";
32
33
34 SGTexture::SGTexture()
35    : texture_id(0),
36      texture_data(0),
37      num_colors(3)
38 {
39 }
40
41 SGTexture::SGTexture(unsigned int width, unsigned int height)
42    : texture_id(0),
43      errstr("")
44 {
45     texture_data = new GLubyte[ width * height * 3 ];
46 }
47
48 SGTexture::~SGTexture()
49 {
50     if ( texture_data ) {
51         delete texture_data;
52     }
53
54     if ( texture_id != 0 ) {
55         free_id();
56     }
57 }
58
59 void
60 SGTexture::bind()
61 {
62     bool gen = false;
63     if (!texture_id) {
64 #ifdef GL_VERSION_1_1
65         glGenTextures(1, &texture_id);
66
67 #elif GL_EXT_texture_object
68         glGenTexturesEXT(1, &texture_id);
69 #endif
70         gen = true;
71     }
72
73 #ifdef GL_VERSION_1_1
74     glBindTexture(GL_TEXTURE_2D, texture_id);
75
76 #elif GL_EXT_texture_object
77     glBindTextureEXT(GL_TEXTURE_2D, texture_id);
78 #endif
79
80     if (gen) {
81         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
82         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
83         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
84         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
85     }
86 }
87
88 /**
89  * A function to resize the OpenGL window which will be used by
90  * the dynamic texture generating routines.
91  *
92  * @param width The width of the new window
93  * @param height The height of the new window
94  */
95 void
96 SGTexture::resize(unsigned int width, unsigned int height)
97 {
98     GLfloat aspect;
99
100     // Make sure that we don't get a divide by zero exception
101     if (height == 0)
102         height = 1;
103
104     // Set the viewport for the OpenGL window
105     glViewport(0, 0, width, height);
106
107     // Calculate the aspect ratio of the window
108     aspect = width/height;
109
110     // Go to the projection matrix, this gets modified by the perspective
111     // calulations
112     glMatrixMode(GL_PROJECTION);
113     glLoadIdentity();
114
115     // Do the perspective calculations
116     gluPerspective(45.0, aspect, 1.0, 400.0);
117
118     // Return to the modelview matrix
119     glMatrixMode(GL_MODELVIEW);
120 }
121
122 /**
123  * A function to prepare the OpenGL state machine for dynamic
124  * texture generation.
125  *
126  * @param width The width of the texture
127  * @param height The height of the texture
128  */
129 void
130 SGTexture::prepare(unsigned int width, unsigned int height) {
131
132     texture_width = width;
133     texture_height = height;
134
135     // Resize the OpenGL window to the size of our dynamic texture
136     resize(texture_width, texture_height);
137
138     glClearColor(0.0, 0.0, 0.0, 1.0);
139 }
140
141 /**
142  * A function to generate the dynamic texture.
143  *
144  * The actual texture can be accessed by calling get_texture()
145  *
146  * @param width The width of the previous OpenGL window
147  * @param height The height of the previous OpenGL window
148  */
149 void
150 SGTexture::finish(unsigned int width, unsigned int height) {
151     // If a texture hasn't been created then it gets created, and the contents
152     // of the frame buffer gets copied into it. If the texture has already been
153     // created then its contents just get updated.
154     bind();
155     if (!texture_data)
156     {
157       // Copies the contents of the frame buffer into the texture
158       glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0,
159                                       texture_width, texture_height, 0);
160
161     } else {
162       // Copies the contents of the frame buffer into the texture
163       glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0,
164                                          texture_width, texture_height);
165     }
166
167     // Set the OpenGL window back to its previous size
168     resize(width, height);
169
170     // Clear the window back to black
171     glClearColor(0.0, 0.0, 0.0, 1.0);
172     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
173 }
174
175
176 void
177 SGTexture::read_alpha_texture(const char *name)
178 {
179     GLubyte *lptr;
180     SGTexture::ImageRec *image;
181     int y;
182
183     if (texture_data)
184         delete texture_data;
185
186     image = ImageOpen(name);
187     if(!image) {
188         errstr = FILE_OPEN_ERROR;
189         return;
190     }
191
192     texture_width = image->xsize;
193     texture_height = image->ysize;
194
195     // printf("image->zsize = %d\n", image->zsize);
196
197     if (image->zsize != 1) {
198       ImageClose(image);
199       errstr = WRONG_COUNT;
200       return;
201     }
202
203     texture_data = new GLubyte[ image->xsize * image->ysize ];
204     num_colors = 1;
205     if (!texture_data) {
206         errstr = NO_TEXTURE;
207         return;
208     }
209
210     lptr = texture_data;
211     for(y=0; y<image->ysize; y++) {
212         ImageGetRow(image,lptr,y,0);
213         lptr += image->xsize;
214     }
215     ImageClose(image);
216 }
217
218 void
219 SGTexture::read_rgb_texture(const char *name)
220 {
221     GLubyte *ptr;
222     GLubyte *rbuf, *gbuf, *bbuf, *abuf;
223     SGTexture::ImageRec *image;
224     int y;
225
226     if (texture_data)
227         delete texture_data;
228
229     image = ImageOpen(name);
230     if(!image) {
231         errstr = FILE_OPEN_ERROR;
232         return;
233     }
234
235     texture_width = image->xsize;
236     texture_height = image->ysize;
237     if (image->zsize != 3 && image->zsize != 4) {
238       ImageClose(image);
239       errstr = WRONG_COUNT;
240       return;
241     }
242
243     texture_data = new GLubyte[ image->xsize * image->ysize * 3 ];
244     num_colors = 3;
245     rbuf = new GLubyte[ image->xsize ];
246     gbuf = new GLubyte[ image->xsize ];
247     bbuf = new GLubyte[ image->xsize ];
248     abuf = new GLubyte[ image->xsize ];
249     if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
250       delete texture_data;
251       delete rbuf;
252       delete gbuf;
253       delete bbuf;
254       delete abuf;
255       errstr = OUT_OF_MEMORY;
256       return;
257     }
258
259     ptr = texture_data;
260     for(y=0; y<image->ysize; y++) {
261         if(image->zsize == 4) {
262             ImageGetRow(image,rbuf,y,0);
263             ImageGetRow(image,gbuf,y,1);
264             ImageGetRow(image,bbuf,y,2);
265             ImageGetRow(image,abuf,y,3); // discard
266             rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
267             ptr += (image->xsize * 3);
268         } else {
269             ImageGetRow(image,rbuf,y,0);
270             ImageGetRow(image,gbuf,y,1);
271             ImageGetRow(image,bbuf,y,2);
272             rgbtorgb(rbuf,gbuf,bbuf,ptr,image->xsize);
273             ptr += (image->xsize * 3);
274         }
275     }
276
277     ImageClose(image);
278     delete rbuf;
279     delete gbuf;
280     delete bbuf;
281     delete abuf;
282 }
283
284
285
286 void
287 SGTexture::read_rgba_texture(const char *name)
288 {
289     GLubyte *ptr;
290     GLubyte *rbuf, *gbuf, *bbuf, *abuf;
291     SGTexture::ImageRec *image;
292     int y;
293
294     if (texture_data)
295         delete texture_data;
296
297     image = ImageOpen(name);
298     if(!image) {
299         errstr = FILE_OPEN_ERROR;
300         return;
301     }
302
303     texture_width = image->xsize;
304     texture_height = image->ysize;
305     if (image->zsize != 3 && image->zsize != 4) {
306       ImageClose(image);
307       errstr = WRONG_COUNT;
308       return;
309     }
310
311     texture_data = new GLubyte[ image->xsize * image->ysize * 4 ];
312     num_colors = 4;
313     rbuf = new GLubyte[ image->xsize ];
314     gbuf = new GLubyte[ image->xsize ];
315     bbuf = new GLubyte[ image->xsize ];
316     abuf = new GLubyte[ image->xsize ];
317     if(!texture_data || !rbuf || !gbuf || !bbuf || !abuf) {
318       delete texture_data;
319       delete rbuf;
320       delete gbuf;
321       delete bbuf;
322       delete abuf;
323       errstr = OUT_OF_MEMORY;
324       return;
325     }
326
327     ptr = texture_data;
328     memset(abuf, 255, image->xsize);
329     for(y=0; y<image->ysize; y++) {
330         if(image->zsize == 4) {
331             ImageGetRow(image,rbuf,y,0);
332             ImageGetRow(image,gbuf,y,1);
333             ImageGetRow(image,bbuf,y,2);
334             ImageGetRow(image,abuf,y,3);
335             rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
336             ptr += (image->xsize * 4);
337         } else {
338             ImageGetRow(image,rbuf,y,0);
339             ImageGetRow(image,gbuf,y,1);
340             ImageGetRow(image,bbuf,y,2);
341             rgbatorgba(rbuf,gbuf,bbuf,abuf,ptr,image->xsize);
342             ptr += (image->xsize * 3);
343         }
344     }
345
346     ImageClose(image);
347     delete rbuf;
348     delete gbuf;
349     delete bbuf;
350     delete abuf;
351 }
352
353 void
354 SGTexture::read_raw_texture(const char *name)
355 {
356     GLubyte *ptr;
357     SGTexture::ImageRec *image;
358     int y;
359
360     if (texture_data)
361         delete texture_data;
362
363     image = RawImageOpen(name);
364
365     if(!image) {
366         errstr = FILE_OPEN_ERROR;
367         return;
368     }
369
370     texture_width = 256;
371     texture_height = 256;
372
373     texture_data = new GLubyte[ 256 * 256 * 3 ];
374     if(!texture_data) {
375       errstr = OUT_OF_MEMORY;
376       return;
377     }
378
379     ptr = texture_data;
380     for(y=0; y<256; y++) {
381         gzread(image->gzfile, ptr, 256*3);
382         ptr+=256*3;
383     }
384     ImageClose(image);
385 }
386
387 void
388 SGTexture::read_r8_texture(const char *name)
389 {
390     unsigned char c[1];
391     GLubyte *ptr;
392     SGTexture::ImageRec *image;
393     int xy;
394
395     if (texture_data)
396         delete texture_data;
397
398     //it wouldn't make sense to write a new function ...
399     image = RawImageOpen(name);
400
401     if(!image) {
402         errstr = FILE_OPEN_ERROR;
403         return;
404     }
405
406     texture_width = 256;
407     texture_height = 256;
408
409     texture_data = new GLubyte [ 256 * 256 * 3 ];
410     if(!texture_data) {
411         errstr = OUT_OF_MEMORY;
412         return;
413     }
414
415     ptr = texture_data;
416     for(xy=0; xy<(256*256); xy++) {
417         gzread(image->gzfile, c, 1);
418
419         //look in the table for the right colours
420         ptr[0]=msfs_colour[c[0]][0];
421         ptr[1]=msfs_colour[c[0]][1];
422         ptr[2]=msfs_colour[c[0]][2];
423
424         ptr+=3;
425     }
426     ImageClose(image);
427 }
428
429
430 void
431 SGTexture::write_texture(const char *name) {
432    SGTexture::ImageRec *image;
433    int x, y;
434
435    image=ImageWriteOpen(name);
436
437    GLubyte *ptr = texture_data;
438    for (y=0; y<texture_height; y++) {
439        for (x=0; x<texture_width; x++) {
440            image->tmp[x]=*ptr;
441            ptr = ptr + num_colors;
442        }
443        fwrite(image->tmp, 1, texture_width, image->file);
444    }
445
446    if (num_colors > 1) {
447       ptr = texture_data + 1;
448       for (y=0; y<texture_height; y++) {
449           for (x=0; x<texture_width; x++) {
450              image->tmp[x]=*ptr;
451              ptr = ptr + num_colors;
452           }
453           fwrite(image->tmp, 1, texture_width, image->file);
454       }
455
456       if (num_colors > 2) {
457          ptr = texture_data + 2;
458          for (y=0; y<texture_height; y++) {
459              for (x=0; x<texture_width; x++) {
460                  image->tmp[x]=*ptr;
461                   ptr = ptr + num_colors;
462              }
463              fwrite(image->tmp, 1, texture_width, image->file);
464          }
465
466          if (num_colors > 3) {
467             ptr = texture_data + 3;
468             for (y=0; y<texture_height; y++) {
469                 for (x=0; x<texture_width; x++) {
470                     image->tmp[x]=*ptr;
471                     ptr = ptr + num_colors;
472                 }
473                 fwrite(image->tmp, 1, texture_width, image->file);
474             }
475          }
476       }
477    }
478
479    ImageClose(image);
480 }
481
482
483 void
484 SGTexture::set_pixel(GLuint x, GLuint y, sgVec3 &c)
485 {
486     if (!texture_data) {
487         errstr = NO_TEXTURE;
488         return;
489     }
490
491     unsigned int pos = (x + y*texture_width)*3;
492     texture_data[pos]   = (unsigned char)(c[0] * 256);
493     texture_data[pos+1] = (unsigned char)(c[1] * 256);
494     texture_data[pos+2] = (unsigned char)(c[2] * 256);
495 }
496
497
498 float *
499 SGTexture::get_pixel(GLuint x, GLuint y)
500 {
501     static sgVec3 c;
502
503     sgSetVec3(c, 0, 0, 0);
504     if (!texture_data) {
505         errstr = NO_TEXTURE;
506         return c;
507     }
508
509     unsigned int pos = (x + y*texture_width)*3;
510
511     sgSetVec3(c, texture_data[pos], texture_data[pos+1], texture_data[pos+2]);
512
513     return c;
514 }
515
516 SGTexture::ImageRec *
517 SGTexture::ImageOpen(const char *fileName)
518 {
519      union {
520        int testWord;
521        char testByte[4];
522      } endianTest;
523
524     SGTexture::ImageRec *image;
525     int swapFlag;
526     int x;
527
528     endianTest.testWord = 1;
529     if (endianTest.testByte[0] == 1) {
530         swapFlag = 1;
531     } else {
532         swapFlag = 0;
533     }
534
535     image = new SGTexture::ImageRec;
536     if (image == 0) {
537         errstr = OUT_OF_MEMORY;
538         return 0;
539     }
540     if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
541       errstr = FILE_OPEN_ERROR;
542       return 0;
543     }
544
545     gzread(image->gzfile, image, 12);
546
547     if (swapFlag) {
548         ConvertShort(&image->imagic, 6);
549     }
550
551     image->tmp = new GLubyte[ image->xsize * 256 ];
552     if (image->tmp == 0) {
553         errstr = OUT_OF_MEMORY;
554         return 0;
555     }
556
557     if ((image->type & 0xFF00) == 0x0100) {
558         x = image->ysize * image->zsize * (int) sizeof(unsigned);
559         image->rowStart = new unsigned[x];
560         image->rowSize = new int[x];
561         if (image->rowStart == 0 || image->rowSize == 0) {
562             errstr = OUT_OF_MEMORY;
563             return 0;
564         }
565         image->rleEnd = 512 + (2 * x);
566         gzseek(image->gzfile, 512, SEEK_SET);
567         gzread(image->gzfile, image->rowStart, x);
568         gzread(image->gzfile, image->rowSize, x);
569         if (swapFlag) {
570             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
571             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
572         }
573     }
574     return image;
575 }
576
577
578 void
579 SGTexture::ImageClose(SGTexture::ImageRec *image) {
580     if (image->gzfile)  gzclose(image->gzfile);
581     if (image->file)    fclose(image->file);
582     delete image->tmp;
583     delete image;
584 }
585
586 SGTexture::ImageRec *
587 SGTexture::RawImageOpen(const char *fileName)
588 {
589      union {
590        int testWord;
591        char testByte[4];
592      } endianTest;
593
594     SGTexture::ImageRec *image;
595     int swapFlag;
596
597     endianTest.testWord = 1;
598     if (endianTest.testByte[0] == 1) {
599         swapFlag = 1;
600     } else {
601         swapFlag = 0;
602     }
603
604     image = new SGTexture::ImageRec;
605     if (image == 0) {
606         errstr = OUT_OF_MEMORY;
607         return 0;
608     }
609     if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
610       errstr = FILE_OPEN_ERROR;
611       return 0;
612     }
613
614     gzread(image->gzfile, image, 12);
615
616     if (swapFlag) {
617         ConvertShort(&image->imagic, 6);
618     }
619
620
621     //just allocate a pseudo value as I'm too lazy to change ImageClose()...
622     image->tmp = new GLubyte;
623
624     if (image->tmp == 0) {
625         errstr = OUT_OF_MEMORY;
626         return 0;
627     }
628
629     return image;
630 }
631
632 SGTexture::ImageRec *
633 SGTexture::ImageWriteOpen(const char *fileName)
634 {
635     union {
636         int testWord;
637         char testByte[4];
638     } endianTest;
639     ImageRec* image;
640     int swapFlag;
641     int x;
642
643     endianTest.testWord = 1;
644     if (endianTest.testByte[0] == 1) {
645         swapFlag = 1;
646     } else {
647         swapFlag = 0;
648     }
649
650     image = new SGTexture::ImageRec;
651     if (image == 0) {
652         errstr = OUT_OF_MEMORY;
653         return 0;
654     }
655     if ((image->file = fopen(fileName, "w")) == 0) {
656         errstr = FILE_OPEN_ERROR;
657         return 0;
658     }
659
660     image->imagic = 474;
661     image->type = 0x0001;
662     image->dim = 0;
663     image->xsize = texture_width;
664     image->ysize = texture_height;
665     image->zsize = num_colors;
666
667     fwrite(image, 1, 12, image->file);
668
669     fseek(image->file, 512, SEEK_SET);
670     if (swapFlag) {
671         ConvertShort(&image->imagic, 6);
672     }
673
674     image->tmp = new GLubyte[ image->xsize * 256 ];
675     if (image->tmp == 0) {
676         errstr = OUT_OF_MEMORY;
677         return 0;
678     }
679
680     if ((image->type & 0xFF00) == 0x0100) {
681         x = image->ysize * image->zsize * (int) sizeof(unsigned);
682         image->rowStart = new unsigned[x];
683         image->rowSize = new int[x];
684         if (image->rowStart == 0 || image->rowSize == 0) {
685             errstr = OUT_OF_MEMORY;
686             return 0;
687         }
688         image->rleEnd = 512 + (2 * x);
689         fseek(image->file, 512, SEEK_SET);
690         fread(image->rowStart, 1, x, image->file);
691         fread(image->rowSize, 1, x, image->file);
692         if (swapFlag) {
693             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
694             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
695         }
696     }
697     return image;
698
699 }
700
701 void
702 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
703     GLubyte *iPtr, *oPtr, pixel;
704     int count;
705
706     if ((image->type & 0xFF00) == 0x0100) {
707         gzseek(image->gzfile, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
708         gzread(image->gzfile, image->tmp,
709                (unsigned int)image->rowSize[y+z*image->ysize]);
710
711         iPtr = image->tmp;
712         oPtr = buf;
713         for (;;) {
714             pixel = *iPtr++;
715             count = (int)(pixel & 0x7F);
716             if (!count) {
717                 errstr = WRONG_COUNT;
718                 return;
719             }
720             if (pixel & 0x80) {
721                 while (count--) {
722                     *oPtr++ = *iPtr++;
723                 }
724             } else {
725                 pixel = *iPtr++;
726                 while (count--) {
727                     *oPtr++ = pixel;
728                 }
729             }
730         }
731     } else {
732         gzseek(image->gzfile, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
733               SEEK_SET);
734         gzread(image->gzfile, buf, image->xsize);
735     }
736 }
737
738 void
739 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
740     GLubyte *iPtr, *oPtr, pixel;
741     int count;
742
743     if ((image->type & 0xFF00) == 0x0100) {
744         fseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
745         fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
746                image->file);
747
748         iPtr = image->tmp;
749         oPtr = buf;
750         for (;;) {
751             pixel = *iPtr++;
752             count = (int)(pixel & 0x7F);
753             if (!count) {
754                 errstr = WRONG_COUNT;
755                 return;
756             }
757             if (pixel & 0x80) {
758                 while (count--) {
759                     *oPtr++ = *iPtr++;
760                 }
761             } else {
762                 pixel = *iPtr++;
763                 while (count--) {
764                     *oPtr++ = pixel;
765                 }
766             }
767         }
768     } else {
769         fseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
770               SEEK_SET);
771         fread(buf, 1, image->xsize, image->file);
772     }
773 }
774
775
776 void
777 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
778     while(n--) {
779         l[0] = r[0];
780         l[1] = g[0];
781         l[2] = b[0];
782         l += 3; r++; g++; b++;
783     }
784 }
785
786 void
787 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
788                       GLubyte *l, int n) {
789     while(n--) {
790         l[0] = r[0];
791         l[1] = g[0];
792         l[2] = b[0];
793         l[3] = a[0];
794         l += 4; r++; g++; b++; a++;
795     }
796 }
797
798
799 void
800 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
801     unsigned short b1, b2;
802     unsigned char *ptr;
803
804     ptr = (unsigned char *)array;
805     while (length--) {
806         b1 = *ptr++;
807         b2 = *ptr++;
808         *array++ = (b1 << 8) | (b2);
809     }
810 }
811
812
813 void
814 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
815     unsigned int b1, b2, b3, b4;
816     unsigned char *ptr;
817
818     ptr = (unsigned char *)array;
819     while (length--) {
820         b1 = *ptr++;
821         b2 = *ptr++;
822         b3 = *ptr++;
823         b4 = *ptr++;
824         *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
825     }
826 }
827