]> git.mxchange.org Git - simgear.git/blob - simgear/screen/texture.cxx
Some small updates to the saving code.
[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 = ImageWriteOpen(name);
433
434    for (int c=0; c<num_colors; c++) {
435       GLubyte *ptr = texture_data + c;
436       for (int y=0; y<texture_height; y++) {
437          for (int x=0; x<texture_width; x++) {
438             image->tmp[x]=*ptr;
439             ptr = ptr + num_colors;
440          }
441          fwrite(image->tmp, 1, texture_width, image->file);
442       }
443    }
444
445    ImageClose(image);
446 }
447
448
449 void
450 SGTexture::set_pixel(GLuint x, GLuint y, sgVec3 &c)
451 {
452     if (!texture_data) {
453         errstr = NO_TEXTURE;
454         return;
455     }
456
457     unsigned int pos = (x + y*texture_width)*3;
458     texture_data[pos]   = (unsigned char)(c[0] * 256);
459     texture_data[pos+1] = (unsigned char)(c[1] * 256);
460     texture_data[pos+2] = (unsigned char)(c[2] * 256);
461 }
462
463
464 float *
465 SGTexture::get_pixel(GLuint x, GLuint y)
466 {
467     static sgVec3 c;
468
469     sgSetVec3(c, 0, 0, 0);
470     if (!texture_data) {
471         errstr = NO_TEXTURE;
472         return c;
473     }
474
475     unsigned int pos = (x + y*texture_width)*3;
476
477     sgSetVec3(c, texture_data[pos], texture_data[pos+1], texture_data[pos+2]);
478
479     return c;
480 }
481
482 SGTexture::ImageRec *
483 SGTexture::ImageOpen(const char *fileName)
484 {
485      union {
486        int testWord;
487        char testByte[4];
488      } endianTest;
489
490     SGTexture::ImageRec *image;
491     int swapFlag;
492     int x;
493
494     endianTest.testWord = 1;
495     if (endianTest.testByte[0] == 1) {
496         swapFlag = 1;
497     } else {
498         swapFlag = 0;
499     }
500
501     image = new SGTexture::ImageRec;
502     memset(image, 0, sizeof(SGTexture::ImageRec));
503     if (image == 0) {
504         errstr = OUT_OF_MEMORY;
505         return 0;
506     }
507     if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
508       errstr = FILE_OPEN_ERROR;
509       return 0;
510     }
511
512     gzread(image->gzfile, image, 12);
513
514     if (swapFlag) {
515         ConvertShort(&image->imagic, 6);
516     }
517
518     image->tmp = new GLubyte[ image->xsize * 256 ];
519     if (image->tmp == 0) {
520         errstr = OUT_OF_MEMORY;
521         return 0;
522     }
523
524     if ((image->type & 0xFF00) == 0x0100) {
525         x = image->ysize * image->zsize * (int) sizeof(unsigned);
526         image->rowStart = new unsigned[x];
527         image->rowSize = new int[x];
528         if (image->rowStart == 0 || image->rowSize == 0) {
529             errstr = OUT_OF_MEMORY;
530             return 0;
531         }
532         image->rleEnd = 512 + (2 * x);
533         gzseek(image->gzfile, 512, SEEK_SET);
534         gzread(image->gzfile, image->rowStart, x);
535         gzread(image->gzfile, image->rowSize, x);
536         if (swapFlag) {
537             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
538             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
539         }
540     }
541     return image;
542 }
543
544
545 void
546 SGTexture::ImageClose(SGTexture::ImageRec *image) {
547     if (image->gzfile)  gzclose(image->gzfile);
548     if (image->file)    fclose(image->file);
549     delete image->tmp;
550     delete image;
551 }
552
553 SGTexture::ImageRec *
554 SGTexture::RawImageOpen(const char *fileName)
555 {
556      union {
557        int testWord;
558        char testByte[4];
559      } endianTest;
560
561     SGTexture::ImageRec *image;
562     int swapFlag;
563
564     endianTest.testWord = 1;
565     if (endianTest.testByte[0] == 1) {
566         swapFlag = 1;
567     } else {
568         swapFlag = 0;
569     }
570
571     image = new SGTexture::ImageRec;
572     memset(image, 0, sizeof(SGTexture::ImageRec));
573     if (image == 0) {
574         errstr = OUT_OF_MEMORY;
575         return 0;
576     }
577     if ((image->gzfile = gzopen(fileName, "rb")) == 0) {
578       errstr = FILE_OPEN_ERROR;
579       return 0;
580     }
581
582     gzread(image->gzfile, image, 12);
583
584     if (swapFlag) {
585         ConvertShort(&image->imagic, 6);
586     }
587
588
589     //just allocate a pseudo value as I'm too lazy to change ImageClose()...
590     image->tmp = new GLubyte;
591
592     if (image->tmp == 0) {
593         errstr = OUT_OF_MEMORY;
594         return 0;
595     }
596
597     return image;
598 }
599
600 SGTexture::ImageRec *
601 SGTexture::ImageWriteOpen(const char *fileName)
602 {
603     union {
604         int testWord;
605         char testByte[4];
606     } endianTest;
607     ImageRec* image;
608     int swapFlag;
609     int x;
610
611     endianTest.testWord = 1;
612     if (endianTest.testByte[0] == 1) {
613         swapFlag = 1;
614     } else {
615         swapFlag = 0;
616     }
617
618     image = new SGTexture::ImageRec;
619     memset(image, 0, sizeof(SGTexture::ImageRec));
620     if (image == 0) {
621         errstr = OUT_OF_MEMORY;
622         return 0;
623     }
624     if ((image->file = fopen(fileName, "wb")) == 0) {
625         errstr = FILE_OPEN_ERROR;
626         return 0;
627     }
628
629     image->imagic = 474;
630     image->type = 0x0001;
631     image->dim = (num_colors > 1) ? 3 : 2;
632     image->xsize = texture_width;
633     image->ysize = texture_height;
634     image->zsize = num_colors;
635
636     fwrite(image, 1, 12, image->file);
637
638     fseek(image->file, 512, SEEK_SET);
639
640     if (swapFlag) {
641         ConvertShort(&image->imagic, 6);
642     }
643
644     image->tmp = new GLubyte[ image->xsize * 256 ];
645     if (image->tmp == 0) {
646         errstr = OUT_OF_MEMORY;
647         return 0;
648     }
649
650     if ((image->type & 0xFF00) == 0x0100) {
651         x = image->ysize * image->zsize * (int) sizeof(unsigned);
652         image->rowStart = new unsigned[x];
653         image->rowSize = new int[x];
654         if (image->rowStart == 0 || image->rowSize == 0) {
655             errstr = OUT_OF_MEMORY;
656             return 0;
657         }
658         image->rleEnd = 512 + (2 * x);
659         fseek(image->file, 512, SEEK_SET);
660         fwrite(image->rowStart, 1, x, image->file);
661         fwrite(image->rowSize, 1, x, image->file);
662         if (swapFlag) {
663             ConvertUint(image->rowStart, x/(int) sizeof(unsigned));
664             ConvertUint((unsigned *)image->rowSize, x/(int) sizeof(int));
665         }
666     }
667
668     return image;
669
670 }
671
672 void
673 SGTexture::ImageGetRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
674     GLubyte *iPtr, *oPtr, pixel;
675     int count;
676
677     if ((image->type & 0xFF00) == 0x0100) {
678         gzseek(image->gzfile, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
679         gzread(image->gzfile, image->tmp,
680                (unsigned int)image->rowSize[y+z*image->ysize]);
681
682         iPtr = image->tmp;
683         oPtr = buf;
684         for (;;) {
685             pixel = *iPtr++;
686             count = (int)(pixel & 0x7F);
687             if (!count) {
688                 errstr = WRONG_COUNT;
689                 return;
690             }
691             if (pixel & 0x80) {
692                 while (count--) {
693                     *oPtr++ = *iPtr++;
694                 }
695             } else {
696                 pixel = *iPtr++;
697                 while (count--) {
698                     *oPtr++ = pixel;
699                 }
700             }
701         }
702     } else {
703         gzseek(image->gzfile, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
704               SEEK_SET);
705         gzread(image->gzfile, buf, image->xsize);
706     }
707 }
708
709 void
710 SGTexture::ImagePutRow(SGTexture::ImageRec *image, GLubyte *buf, int y, int z) {
711     GLubyte *iPtr, *oPtr, pixel;
712     int count;
713
714     if ((image->type & 0xFF00) == 0x0100) {
715         fseek(image->file, (long) image->rowStart[y+z*image->ysize], SEEK_SET);
716         fread( image->tmp, 1, (unsigned int)image->rowSize[y+z*image->ysize],
717                image->file);
718
719         iPtr = image->tmp;
720         oPtr = buf;
721         for (;;) {
722             pixel = *iPtr++;
723             count = (int)(pixel & 0x7F);
724             if (!count) {
725                 errstr = WRONG_COUNT;
726                 return;
727             }
728             if (pixel & 0x80) {
729                 while (count--) {
730                     *oPtr++ = *iPtr++;
731                 }
732             } else {
733                 pixel = *iPtr++;
734                 while (count--) {
735                     *oPtr++ = pixel;
736                 }
737             }
738         }
739     } else {
740         fseek(image->file, 512+(y*image->xsize)+(z*image->xsize*image->ysize),
741               SEEK_SET);
742         fread(buf, 1, image->xsize, image->file);
743     }
744 }
745
746
747 void
748 SGTexture::rgbtorgb(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *l, int n) {
749     while(n--) {
750         l[0] = r[0];
751         l[1] = g[0];
752         l[2] = b[0];
753         l += 3; r++; g++; b++;
754     }
755 }
756
757 void
758 SGTexture::rgbatorgba(GLubyte *r, GLubyte *g, GLubyte *b, GLubyte *a,
759                       GLubyte *l, int n) {
760     while(n--) {
761         l[0] = r[0];
762         l[1] = g[0];
763         l[2] = b[0];
764         l[3] = a[0];
765         l += 4; r++; g++; b++; a++;
766     }
767 }
768
769
770 void
771 SGTexture::ConvertShort(unsigned short *array, unsigned int length) {
772     unsigned short b1, b2;
773     unsigned char *ptr;
774
775     ptr = (unsigned char *)array;
776     while (length--) {
777         b1 = *ptr++;
778         b2 = *ptr++;
779         *array++ = (b1 << 8) | (b2);
780     }
781 }
782
783
784 void
785 SGTexture::ConvertUint(unsigned *array, unsigned int length) {
786     unsigned int b1, b2, b3, b4;
787     unsigned char *ptr;
788
789     ptr = (unsigned char *)array;
790     while (length--) {
791         b1 = *ptr++;
792         b2 = *ptr++;
793         b3 = *ptr++;
794         b4 = *ptr++;
795         *array++ = (b1 << 24) | (b2 << 16) | (b3 << 8) | (b4);
796     }
797 }
798