]> git.jsancho.org Git - lugaru.git/blob - Source/OpenGL_Windows.cpp
2c7272d5ec377337a3e27ac6e4eacc929afdad7c
[lugaru.git] / Source / OpenGL_Windows.cpp
1 /*
2 Copyright (C) 2003, 2010 - Wolfire Games
3
4 This file is part of Lugaru.
5
6 Lugaru is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 as published by the Free Software Foundation; either version 2
9 of the 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.
14
15 See the GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21
22
23 #ifdef WIN32
24 #include <windows.h>
25 #endif
26
27 #include "Game.h"
28
29 #include <jpeglib.h>
30 #include <png.h>
31 #include <zlib.h>
32
33 using namespace Game;
34
35 static bool load_image(const char * fname, TGAImageRec & tex);
36 static bool load_png(const char * fname, TGAImageRec & tex);
37 static bool load_jpg(const char * fname, TGAImageRec & tex);
38 bool save_image(const char * fname);
39 static bool save_png(const char * fname);
40
41
42 #include "openal_wrapper.h"
43
44 extern float multiplier;
45 extern float sps;
46 extern float realmultiplier;
47 extern int slomo;
48 extern bool cellophane;
49 extern float texdetail;
50
51 extern bool osx;
52 extern bool freeze;
53 extern bool stillloading;
54 extern int mainmenu;
55 /*extern*/
56 bool gameFocused;
57
58 extern float slomospeed;
59 extern float slomofreq;
60
61
62
63 #include <math.h>
64 #include <stdio.h>
65 #include <string.h>
66 #include <fstream>
67 #include <iostream>
68 #include <set>
69 #include "gamegl.h"
70 #include "MacCompatibility.h"
71 #include "Settings.h"
72
73 #ifdef WIN32
74 #include <shellapi.h>
75 #include "win-res/resource.h"
76 #endif
77
78 extern SDL_Window *sdlwindow;
79
80 using namespace std;
81
82 set<pair<int,int>> resolutions;
83
84 Boolean SetUp ();
85 void DoUpdate ();
86
87 void CleanUp (void);
88
89 // statics/globals (internal only) ------------------------------------------
90
91 #ifdef _MSC_VER
92 #pragma warning(push)
93 #pragma warning(disable: 4273)
94 #endif
95
96 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
97 #define GL_FUNC(ret,fn,params,call,rt) \
98     extern "C" { \
99         static ret (GLAPIENTRY *p##fn) params = NULL; \
100         ret GLAPIENTRY fn params { rt p##fn call; } \
101     }
102 #include "glstubs.h"
103 #undef GL_FUNC
104 #endif // __MINGW32__
105
106 #ifdef _MSC_VER
107 #pragma warning(pop)
108 #endif
109
110 static bool lookup_glsym(const char *funcname, void **func)
111 {
112     *func = SDL_GL_GetProcAddress(funcname);
113     if (*func == NULL) {
114         fprintf(stderr, "Failed to find OpenGL symbol \"%s\"\n", funcname);
115         return false;
116     }
117     return true;
118 }
119
120 static bool lookup_all_glsyms(void)
121 {
122     bool retval = true;
123 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
124 #define GL_FUNC(ret,fn,params,call,rt) \
125         if (!lookup_glsym(#fn, (void **) &p##fn)) retval = false;
126 #include "glstubs.h"
127 #undef GL_FUNC
128 #endif // __MINGW32__
129     return retval;
130 }
131
132 static void GLAPIENTRY glDeleteTextures_doNothing(GLsizei n, const GLuint *textures)
133 {
134     // no-op.
135 }
136
137 #ifdef MessageBox
138 #undef MessageBox
139 #endif
140 #define MessageBox(hwnd,text,title,flags) STUBBED("msgbox")
141
142 // Menu defs
143
144 int kContextWidth;
145 int kContextHeight;
146
147 Boolean gDone = false;
148
149 static int _argc = 0;
150 static char **_argv = NULL;
151
152 bool cmdline(const char *cmd)
153 {
154     for (int i = 1; i < _argc; i++) {
155         char *arg = _argv[i];
156         while (*arg == '-')
157             arg++;
158         if (strcasecmp(arg, cmd) == 0)
159             return true;
160     }
161
162     return false;
163 }
164
165 //-----------------------------------------------------------------------------------------------------------------------
166
167 // OpenGL Drawing
168
169 void initGL()
170 {
171     glClear( GL_COLOR_BUFFER_BIT );
172     swap_gl_buffers();
173
174     // clear all states
175     glDisable( GL_ALPHA_TEST);
176     glDisable( GL_BLEND);
177     glDisable( GL_DEPTH_TEST);
178     //glDisable( GL_DITHER);
179     glDisable( GL_FOG);
180     glDisable( GL_LIGHTING);
181     glDisable( GL_LOGIC_OP);
182     glDisable( GL_TEXTURE_1D);
183     glDisable( GL_TEXTURE_2D);
184     glPixelTransferi( GL_MAP_COLOR, GL_FALSE);
185     glPixelTransferi( GL_RED_SCALE, 1);
186     glPixelTransferi( GL_RED_BIAS, 0);
187     glPixelTransferi( GL_GREEN_SCALE, 1);
188     glPixelTransferi( GL_GREEN_BIAS, 0);
189     glPixelTransferi( GL_BLUE_SCALE, 1);
190     glPixelTransferi( GL_BLUE_BIAS, 0);
191     glPixelTransferi( GL_ALPHA_SCALE, 1);
192     glPixelTransferi( GL_ALPHA_BIAS, 0);
193
194     // set initial rendering states
195     glShadeModel( GL_SMOOTH);
196     glClearDepth( 1.0f);
197     glDepthFunc( GL_LEQUAL);
198     glDepthMask( GL_TRUE);
199     //glDepthRange( FRONT_CLIP, BACK_CLIP);
200     glEnable( GL_DEPTH_TEST);
201     glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
202     glCullFace( GL_FRONT);
203     glEnable( GL_CULL_FACE);
204     glEnable( GL_LIGHTING);
205     //glEnable( GL_LIGHT_MODEL_AMBIENT);
206     glEnable( GL_DITHER);
207     glEnable( GL_COLOR_MATERIAL);
208     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
209     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
210     glAlphaFunc( GL_GREATER, 0.5f);
211
212     if ( CanInitStereo(stereomode) ) {
213         InitStereo(stereomode);
214     } else {
215         fprintf(stderr, "Failed to initialize stereo, disabling.\n");
216         stereomode = stereoNone;
217     }
218 }
219
220 void toggleFullscreen()
221 {
222     fullscreen = !fullscreen;
223     Uint32 flags = SDL_GetWindowFlags(sdlwindow);
224     if (flags & SDL_WINDOW_FULLSCREEN) {
225         flags &= ~SDL_WINDOW_FULLSCREEN;
226     } else {
227         flags |= SDL_WINDOW_FULLSCREEN;
228     }
229     SDL_SetWindowFullscreen(sdlwindow, flags);
230 }
231
232 static SDL_bool sdlEventProc(const SDL_Event &e)
233 {
234     switch (e.type) {
235         case SDL_QUIT:
236             return SDL_FALSE;
237
238         case SDL_WINDOWEVENT:
239             if (e.window.event == SDL_WINDOWEVENT_CLOSE) {
240                 return SDL_FALSE;
241             }
242         break;
243
244         case SDL_MOUSEMOTION:
245             deltah += e.motion.xrel;
246             deltav += e.motion.yrel;
247         break;
248
249         case SDL_KEYDOWN:
250             if ((e.key.keysym.scancode == SDL_SCANCODE_G) &&
251                 (e.key.keysym.mod & KMOD_CTRL)) {
252                 SDL_bool mode = SDL_TRUE;
253                 if ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_FULLSCREEN) == 0)
254                     mode = (SDL_GetWindowGrab(sdlwindow) ? SDL_FALSE : SDL_TRUE);
255                 SDL_SetWindowGrab(sdlwindow, mode);
256                 SDL_SetRelativeMouseMode(mode);
257             } else if ( (e.key.keysym.scancode == SDL_SCANCODE_RETURN) && (e.key.keysym.mod & KMOD_ALT) ) {
258                 toggleFullscreen();
259             }
260         break;
261     }
262     return SDL_TRUE;
263 }
264
265
266
267 // --------------------------------------------------------------------------
268
269 static Point gMidPoint;
270
271 Boolean SetUp ()
272 {
273     char string[10];
274
275     LOGFUNC;
276
277     osx = 0;
278     cellophane = 0;
279     texdetail = 4;
280     slomospeed = 0.25;
281     slomofreq = 8012;
282
283     DefaultSettings();
284
285     if (!SDL_WasInit(SDL_INIT_VIDEO))
286         if (SDL_Init(SDL_INIT_VIDEO) == -1) {
287             fprintf(stderr, "SDL_Init() failed: %s\n", SDL_GetError());
288             return false;
289         }
290     if (!LoadSettings()) {
291         fprintf(stderr, "Failed to load config, creating default\n");
292         SaveSettings();
293     }
294     if (kBitsPerPixel != 32 && kBitsPerPixel != 16) {
295         kBitsPerPixel = 16;
296     }
297
298     if (SDL_GL_LoadLibrary(NULL) == -1) {
299         fprintf(stderr, "SDL_GL_LoadLibrary() failed: %s\n", SDL_GetError());
300         SDL_Quit();
301         return false;
302     }
303
304     for (int displayIdx = 0; displayIdx < SDL_GetNumVideoDisplays(); ++displayIdx) {
305         for (int i = 0; i < SDL_GetNumDisplayModes(displayIdx); ++i) {
306             SDL_DisplayMode mode;
307             if (SDL_GetDisplayMode(displayIdx, i, &mode) == -1)
308                 continue;
309             if ((mode.w < 640) || (mode.h < 480))
310                 continue;  // sane lower limit.
311             pair<int,int> resolution(mode.w, mode.h);
312             resolutions.insert(resolution);
313         }
314     }
315
316     if (resolutions.empty()) {
317         const std::string error = "No suitable video resolutions found.";
318         cerr << error << endl;
319         SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Lugaru init failed!", error.c_str(), NULL);
320         SDL_Quit();
321         return false;
322     }
323
324     if (cmdline("showresolutions")) {
325         printf("Available resolutions:\n");
326         for (auto resolution = resolutions.begin(); resolution != resolutions.end(); resolution++) {
327             printf("  %d x %d\n", (int) resolution->first, (int) resolution->second);
328         }
329     }
330
331     SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
332     SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
333
334     Uint32 sdlflags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN;
335     if ((fullscreen || cmdline("fullscreen")) && !cmdline("windowed")) {
336         fullscreen = 1;
337         sdlflags |= SDL_WINDOW_FULLSCREEN;
338     }
339     if (!cmdline("nomousegrab"))
340         sdlflags |= SDL_WINDOW_INPUT_GRABBED;
341
342     sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
343                                  kContextWidth, kContextHeight, sdlflags);
344
345     if (!sdlwindow) {
346         fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
347         fprintf(stderr, "forcing 640x480...\n");
348         kContextWidth = 640;
349         kContextHeight = 480;
350         sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
351                                      kContextWidth, kContextHeight, sdlflags);
352         if (!sdlwindow) {
353             fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
354             fprintf(stderr, "forcing 640x480 windowed mode...\n");
355             sdlflags &= ~SDL_WINDOW_FULLSCREEN;
356             sdlwindow = SDL_CreateWindow("Lugaru", SDL_WINDOWPOS_CENTERED_DISPLAY(0), SDL_WINDOWPOS_CENTERED_DISPLAY(0),
357                                          kContextWidth, kContextHeight, sdlflags);
358
359             if (!sdlwindow) {
360                 fprintf(stderr, "SDL_CreateWindow() failed: %s\n", SDL_GetError());
361                 return false;
362             }
363         }
364     }
365
366     SDL_GLContext glctx = SDL_GL_CreateContext(sdlwindow);
367     if (!glctx) {
368         fprintf(stderr, "SDL_GL_CreateContext() failed: %s\n", SDL_GetError());
369         SDL_Quit();
370         return false;
371     }
372
373     SDL_GL_MakeCurrent(sdlwindow, glctx);
374
375     if (!lookup_all_glsyms()) {
376         fprintf(stderr, "Missing required OpenGL functions.\n");
377         SDL_Quit();
378         return false;
379     }
380
381     int dblbuf = 0;
382     if ((SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &dblbuf) == -1) || (!dblbuf))
383     {
384         fprintf(stderr, "Failed to get a double-buffered context.\n");
385         SDL_Quit();
386         return false;
387     }
388
389     if (SDL_GL_SetSwapInterval(-1) == -1)  // try swap_tear first.
390         SDL_GL_SetSwapInterval(1);
391
392     SDL_ShowCursor(0);
393     if (!cmdline("nomousegrab"))
394         SDL_SetRelativeMouseMode(SDL_TRUE);
395
396     initGL();
397
398     GLint width = kContextWidth;
399     GLint height = kContextHeight;
400     gMidPoint.h = width / 2;
401     gMidPoint.v = height / 2;
402     screenwidth = width;
403     screenheight = height;
404
405     newdetail = detail;
406     newscreenwidth = screenwidth;
407     newscreenheight = screenheight;
408
409     /* If saved resolution is not in the list, add it to the list (so that it’s selectable in the options) */
410     pair<int,int> startresolution(width,height);
411     if (resolutions.find(startresolution) == resolutions.end()) {
412         resolutions.insert(startresolution);
413     }
414
415     InitGame();
416
417     return true;
418 }
419
420
421 static void DoMouse()
422 {
423
424     if (mainmenu || ( (abs(deltah) < 10 * realmultiplier * 1000) && (abs(deltav) < 10 * realmultiplier * 1000) )) {
425         deltah *= usermousesensitivity;
426         deltav *= usermousesensitivity;
427         mousecoordh += deltah;
428         mousecoordv += deltav;
429         if (mousecoordh < 0)
430             mousecoordh = 0;
431         else if (mousecoordh >= kContextWidth)
432             mousecoordh = kContextWidth - 1;
433         if (mousecoordv < 0)
434             mousecoordv = 0;
435         else if (mousecoordv >= kContextHeight)
436             mousecoordv = kContextHeight - 1;
437     }
438
439 }
440
441 void DoFrameRate (int update)
442 {
443     static long frames = 0;
444
445     static AbsoluteTime time = {0, 0};
446     static AbsoluteTime frametime = {0, 0};
447     AbsoluteTime currTime = UpTime ();
448     double deltaTime = (float) AbsoluteDeltaToDuration (currTime, frametime);
449
450     if (0 > deltaTime) // if negative microseconds
451         deltaTime /= -1000000.0;
452     else // else milliseconds
453         deltaTime /= 1000.0;
454
455     multiplier = deltaTime;
456     if (multiplier < .001)
457         multiplier = .001;
458     if (multiplier > 10)
459         multiplier = 10;
460     if (update)
461         frametime = currTime; // reset for next time interval
462
463     deltaTime = (float) AbsoluteDeltaToDuration (currTime, time);
464
465     if (0 > deltaTime) // if negative microseconds
466         deltaTime /= -1000000.0;
467     else // else milliseconds
468         deltaTime /= 1000.0;
469     frames++;
470     if (0.001 <= deltaTime) { // has update interval passed
471         if (update) {
472             time = currTime; // reset for next time interval
473             frames = 0;
474         }
475     }
476 }
477
478
479 void DoUpdate ()
480 {
481     static float sps = 200;
482     static int count;
483     static float oldmult;
484
485     DoFrameRate(1);
486     if (multiplier > .6)
487         multiplier = .6;
488
489     fps = 1 / multiplier;
490
491     count = multiplier * sps;
492     if (count < 2)
493         count = 2;
494
495     realmultiplier = multiplier;
496     multiplier *= gamespeed;
497     if (difficulty == 1)
498         multiplier *= .9;
499     if (difficulty == 0)
500         multiplier *= .8;
501
502     if (loading == 4)
503         multiplier *= .00001;
504     if (slomo && !mainmenu)
505         multiplier *= slomospeed;
506     oldmult = multiplier;
507     multiplier /= (float)count;
508
509     DoMouse();
510
511     TickOnce();
512
513     for (int i = 0; i < count; i++) {
514         Tick();
515     }
516     multiplier = oldmult;
517
518     TickOnceAfter();
519     /* - Debug code to test how many channels were active on average per frame
520         static long frames = 0;
521
522         static AbsoluteTime start = {0,0};
523         AbsoluteTime currTime = UpTime ();
524         static int num_channels = 0;
525
526         num_channels += OPENAL_GetChannelsPlaying();
527         double deltaTime = (float) AbsoluteDeltaToDuration (currTime, start);
528
529         if (0 > deltaTime)  // if negative microseconds
530             deltaTime /= -1000000.0;
531         else                // else milliseconds
532             deltaTime /= 1000.0;
533
534         ++frames;
535
536         if (deltaTime >= 1)
537         {
538             start = currTime;
539             float avg_channels = (float)num_channels / (float)frames;
540
541             ofstream opstream("log.txt",ios::app);
542             opstream << "Average frame count: ";
543             opstream << frames;
544             opstream << " frames - ";
545             opstream << avg_channels;
546             opstream << " per frame.\n";
547             opstream.close();
548
549             frames = 0;
550             num_channels = 0;
551         }
552     */
553     if ( stereomode == stereoNone ) {
554         DrawGLScene(stereoCenter);
555     } else {
556         DrawGLScene(stereoLeft);
557         DrawGLScene(stereoRight);
558     }
559 }
560
561 // --------------------------------------------------------------------------
562
563
564 void CleanUp (void)
565 {
566     LOGFUNC;
567
568     SDL_Quit();
569 #ifndef __MINGW32__ // FIXME: Temporary workaround for GL-8
570 #define GL_FUNC(ret,fn,params,call,rt) p##fn = NULL;
571 #include "glstubs.h"
572 #undef GL_FUNC
573     // cheat here...static destructors are calling glDeleteTexture() after
574     //  the context is destroyed and libGL unloaded by SDL_Quit().
575     pglDeleteTextures = glDeleteTextures_doNothing;
576 #endif // __MINGW32__
577
578 }
579
580 // --------------------------------------------------------------------------
581
582 static bool IsFocused()
583 {
584     return ((SDL_GetWindowFlags(sdlwindow) & SDL_WINDOW_INPUT_FOCUS) != 0);
585 }
586
587
588
589 #ifndef WIN32
590 // (code lifted from physfs: http://icculus.org/physfs/ ... zlib license.)
591 static char *findBinaryInPath(const char *bin, char *envr)
592 {
593     size_t alloc_size = 0;
594     char *exe = NULL;
595     char *start = envr;
596     char *ptr;
597
598     do {
599         size_t size;
600         ptr = strchr(start, ':');  /* find next $PATH separator. */
601         if (ptr)
602             *ptr = '\0';
603
604         size = strlen(start) + strlen(bin) + 2;
605         if (size > alloc_size) {
606             char *x = (char *) realloc(exe, size);
607             if (x == NULL) {
608                 if (exe != NULL)
609                     free(exe);
610                 return(NULL);
611             } /* if */
612
613             alloc_size = size;
614             exe = x;
615         } /* if */
616
617         /* build full binary path... */
618         strcpy(exe, start);
619         if ((exe[0] == '\0') || (exe[strlen(exe) - 1] != '/'))
620             strcat(exe, "/");
621         strcat(exe, bin);
622
623         if (access(exe, X_OK) == 0) { /* Exists as executable? We're done. */
624             strcpy(exe, start);  /* i'm lazy. piss off. */
625             return(exe);
626         } /* if */
627
628         start = ptr + 1;  /* start points to beginning of next element. */
629     } while (ptr != NULL);
630
631     if (exe != NULL)
632         free(exe);
633
634     return(NULL);  /* doesn't exist in path. */
635 } /* findBinaryInPath */
636
637
638 char *calcBaseDir(const char *argv0)
639 {
640     /* If there isn't a path on argv0, then look through the $PATH for it. */
641     char *retval;
642     char *envr;
643
644     const char *ptr = strrchr((char *)argv0, '/');
645     if (strchr(argv0, '/')) {
646         retval = strdup(argv0);
647         if (retval)
648             *((char *) strrchr(retval, '/')) = '\0';
649         return(retval);
650     }
651
652     envr = getenv("PATH");
653     if (!envr)
654         return NULL;
655     envr = strdup(envr);
656     if (!envr)
657         return NULL;
658     retval = findBinaryInPath(argv0, envr);
659     free(envr);
660     return(retval);
661 }
662
663 static inline void chdirToAppPath(const char *argv0)
664 {
665     char *dir = calcBaseDir(argv0);
666     if (dir) {
667 #if (defined(__APPLE__) && defined(__MACH__))
668         // Chop off /Contents/MacOS if it's at the end of the string, so we
669         //  land in the base of the app bundle.
670         const size_t len = strlen(dir);
671         const char *bundledirs = "/Contents/MacOS";
672         const size_t bundledirslen = strlen(bundledirs);
673         if (len > bundledirslen) {
674             char *ptr = (dir + len) - bundledirslen;
675             if (strcasecmp(ptr, bundledirs) == 0)
676                 *ptr = '\0';
677         }
678 #endif
679         chdir(dir);
680         free(dir);
681     }
682 }
683 #endif
684
685
686 int main(int argc, char **argv)
687 {
688     _argc = argc;
689     _argv = argv;
690
691     // !!! FIXME: we could use a Win32 API for this.  --ryan.
692 #ifndef WIN32
693     chdirToAppPath(argv[0]);
694 #endif
695
696     LOGFUNC;
697
698     try {
699         {
700             newGame();
701
702             //ofstream os("error.txt");
703             //os.close();
704             //ofstream os("log.txt");
705             //os.close();
706
707             if (!SetUp ())
708                 return 42;
709
710             while (!gDone && !tryquit) {
711                 if (IsFocused()) {
712                     gameFocused = true;
713
714                     // check windows messages
715
716                     deltah = 0;
717                     deltav = 0;
718                     SDL_Event e;
719                     if (!waiting) {
720                         // message pump
721                         while ( SDL_PollEvent( &e ) ) {
722                             if (!sdlEventProc(e)) {
723                                 gDone = true;
724                                 break;
725                             }
726                         }
727                     }
728
729                     // game
730                     DoUpdate();
731                 } else {
732                     if (gameFocused) {
733                         // allow game chance to pause
734                         gameFocused = false;
735                         DoUpdate();
736                     }
737
738                     // game is not in focus, give CPU time to other apps by waiting for messages instead of 'peeking'
739                     SDL_WaitEvent(0);
740                 }
741             }
742
743             deleteGame();
744         }
745
746         CleanUp ();
747
748         return 0;
749     } catch (const std::exception& error) {
750         CleanUp();
751
752         std::string e = "Caught exception: ";
753         e += error.what();
754
755         LOG(e);
756
757         MessageBox(g_windowHandle, error.what(), "ERROR", MB_OK | MB_ICONEXCLAMATION);
758     }
759
760     CleanUp();
761
762     return -1;
763 }
764
765
766
767 // --------------------------------------------------------------------------
768
769
770 bool LoadImage(const char * fname, TGAImageRec & tex)
771 {
772     if ( tex.data == NULL )
773         return false;
774     else
775         return load_image(fname, tex);
776 }
777
778 void ScreenShot(const char * fname)
779 {
780
781 }
782
783
784
785 static bool load_image(const char *file_name, TGAImageRec &tex)
786 {
787     const char *ptr = strrchr((char *)file_name, '.');
788     if (ptr) {
789         if (strcasecmp(ptr + 1, "png") == 0)
790             return load_png(file_name, tex);
791         else if (strcasecmp(ptr + 1, "jpg") == 0)
792             return load_jpg(file_name, tex);
793     }
794
795     STUBBED("Unsupported image type");
796     return false;
797 }
798
799
800 struct my_error_mgr {
801     struct jpeg_error_mgr pub; /* "public" fields */
802     jmp_buf setjmp_buffer; /* for return to caller */
803 };
804 typedef struct my_error_mgr * my_error_ptr;
805
806
807 static void my_error_exit(j_common_ptr cinfo)
808 {
809     struct my_error_mgr *err = (struct my_error_mgr *)cinfo->err;
810     longjmp(err->setjmp_buffer, 1);
811 }
812
813 /* stolen from public domain example.c code in libjpg distribution. */
814 static bool load_jpg(const char *file_name, TGAImageRec &tex)
815 {
816     struct jpeg_decompress_struct cinfo;
817     struct my_error_mgr jerr;
818     JSAMPROW buffer[1]; /* Output row buffer */
819     int row_stride; /* physical row width in output buffer */
820     FILE *infile = fopen(file_name, "rb");
821
822     if (infile == NULL)
823         return false;
824
825     cinfo.err = jpeg_std_error(&jerr.pub);
826     jerr.pub.error_exit = my_error_exit;
827     if (setjmp(jerr.setjmp_buffer)) {
828         jpeg_destroy_decompress(&cinfo);
829         fclose(infile);
830         return false;
831     }
832
833     jpeg_create_decompress(&cinfo);
834     jpeg_stdio_src(&cinfo, infile);
835     (void) jpeg_read_header(&cinfo, TRUE);
836
837     cinfo.out_color_space = JCS_RGB;
838     cinfo.quantize_colors = 0;
839     (void) jpeg_calc_output_dimensions(&cinfo);
840     (void) jpeg_start_decompress(&cinfo);
841
842     row_stride = cinfo.output_width * cinfo.output_components;
843     tex.sizeX = cinfo.output_width;
844     tex.sizeY = cinfo.output_height;
845     tex.bpp = 24;
846
847     while (cinfo.output_scanline < cinfo.output_height) {
848         buffer[0] = (JSAMPROW)(char *)tex.data +
849                     ((cinfo.output_height - 1) - cinfo.output_scanline) * row_stride;
850         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
851     }
852
853     (void) jpeg_finish_decompress(&cinfo);
854     jpeg_destroy_decompress(&cinfo);
855     fclose(infile);
856
857     return true;
858 }
859
860
861 /* stolen from public domain example.c code in libpng distribution. */
862 static bool load_png(const char *file_name, TGAImageRec &tex)
863 {
864     bool hasalpha = false;
865     png_structp png_ptr = NULL;
866     png_infop info_ptr = NULL;
867     png_uint_32 width, height;
868     int bit_depth, color_type, interlace_type;
869     png_byte **rows = NULL;
870     bool retval = false;
871     png_byte **row_pointers = NULL;
872     FILE *fp = fopen(file_name, "rb");
873
874     if (fp == NULL) {
875         cerr << file_name << " not found" << endl;
876         return(NULL);
877     }
878
879     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
880     if (png_ptr == NULL)
881         goto png_done;
882
883     info_ptr = png_create_info_struct(png_ptr);
884     if (info_ptr == NULL)
885         goto png_done;
886
887     if (setjmp(png_jmpbuf(png_ptr)))
888         goto png_done;
889
890     png_init_io(png_ptr, fp);
891     png_read_png(png_ptr, info_ptr,
892                  PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_PACKING,
893                  NULL);
894     png_get_IHDR(png_ptr, info_ptr, &width, &height,
895                  &bit_depth, &color_type, &interlace_type, NULL, NULL);
896
897     if (bit_depth != 8)  // transform SHOULD handle this...
898         goto png_done;
899
900     if (color_type & PNG_COLOR_MASK_PALETTE)  // !!! FIXME?
901         goto png_done;
902
903     if ((color_type & PNG_COLOR_MASK_COLOR) == 0)  // !!! FIXME?
904         goto png_done;
905
906     hasalpha = ((color_type & PNG_COLOR_MASK_ALPHA) != 0);
907     row_pointers = png_get_rows(png_ptr, info_ptr);
908     if (!row_pointers)
909         goto png_done;
910
911     if (!hasalpha) {
912         png_byte *dst = tex.data;
913         for (int i = height - 1; i >= 0; i--) {
914             png_byte *src = row_pointers[i];
915             for (int j = 0; j < width; j++) {
916                 dst[0] = src[0];
917                 dst[1] = src[1];
918                 dst[2] = src[2];
919                 dst[3] = 0xFF;
920                 src += 3;
921                 dst += 4;
922             }
923         }
924     }
925
926     else {
927         png_byte *dst = tex.data;
928         int pitch = width * 4;
929         for (int i = height - 1; i >= 0; i--, dst += pitch)
930             memcpy(dst, row_pointers[i], pitch);
931     }
932
933     tex.sizeX = width;
934     tex.sizeY = height;
935     tex.bpp = 32;
936     retval = true;
937
938 png_done:
939     if (!retval) {
940         cerr << "There was a problem loading " << file_name << endl;
941     }
942     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
943     if (fp)
944         fclose(fp);
945     return (retval);
946 }
947
948
949 bool save_image(const char *file_name)
950 {
951     const char *ptr = strrchr((char *)file_name, '.');
952     if (ptr) {
953         if (strcasecmp(ptr + 1, "png") == 0)
954             return save_png(file_name);
955     }
956
957     STUBBED("Unsupported image type");
958     return false;
959 }
960
961
962 static bool save_png(const char *file_name)
963 {
964     FILE *fp = NULL;
965     png_structp png_ptr = NULL;
966     png_infop info_ptr = NULL;
967     bool retval = false;
968
969     fp = fopen(file_name, "wb");
970     if (fp == NULL)
971         return false;
972
973     png_bytep *row_pointers = new png_bytep[kContextHeight];
974     png_bytep screenshot = new png_byte[kContextWidth * kContextHeight * 3];
975     if ((!screenshot) || (!row_pointers))
976         goto save_png_done;
977
978     glGetError();
979     glReadPixels(0, 0, kContextWidth, kContextHeight,
980                  GL_RGB, GL_UNSIGNED_BYTE, screenshot);
981     if (glGetError() != GL_NO_ERROR)
982         goto save_png_done;
983
984     for (int i = 0; i < kContextHeight; i++)
985         row_pointers[i] = screenshot + ((kContextWidth * ((kContextHeight - 1) - i)) * 3);
986
987     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
988     if (png_ptr == NULL)
989         goto save_png_done;
990
991     info_ptr = png_create_info_struct(png_ptr);
992     if (info_ptr == NULL)
993         goto save_png_done;
994
995     if (setjmp(png_jmpbuf(png_ptr)))
996         goto save_png_done;
997
998     png_init_io(png_ptr, fp);
999
1000     if (setjmp(png_jmpbuf(png_ptr)))
1001         goto save_png_done;
1002
1003     png_set_IHDR(png_ptr, info_ptr, kContextWidth, kContextHeight,
1004                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
1005                  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1006
1007     png_write_info(png_ptr, info_ptr);
1008
1009     if (setjmp(png_jmpbuf(png_ptr)))
1010         goto save_png_done;
1011
1012     png_write_image(png_ptr, row_pointers);
1013
1014     if (setjmp(png_jmpbuf(png_ptr)))
1015         goto save_png_done;
1016
1017     png_write_end(png_ptr, NULL);
1018     retval = true;
1019
1020 save_png_done:
1021     png_destroy_write_struct(&png_ptr, &info_ptr);
1022     delete[] screenshot;
1023     delete[] row_pointers;
1024     if (fp)
1025         fclose(fp);
1026     if (!retval)
1027         unlink(ConvertFileName(file_name));
1028     return retval;
1029 }
1030
1031
1032