]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/grsoft.cpp
stub/warning removal.
[taylor/freespace2.git] / src / graphics / grsoft.cpp
1 /*
2  * $Logfile: /Freespace2/code/Graphics/GrSoft.cpp $
3  * $Revision$
4  * $Date$
5  * $Author$
6  *
7  * Code for our software renderer using standard Win32 functions.  (Dibsections, etc)
8  *
9  * $Log$
10  * Revision 1.7  2002/06/05 08:05:29  relnev
11  * stub/warning removal.
12  *
13  * reworked the sound code.
14  *
15  * Revision 1.6  2002/05/28 21:36:10  relnev
16  * some more timer junk.
17  *
18  * tried to fix software mode.
19  *
20  * Revision 1.5  2002/05/28 17:26:57  theoddone33
21  * Fill in some timer and palette setting stubs.  Still no display
22  *
23  * Revision 1.4  2002/05/28 17:03:29  theoddone33
24  * fs2 gets to the main game loop now
25  *
26  * Revision 1.3  2002/05/28 04:56:51  theoddone33
27  * runs a little bit now
28  *
29  * Revision 1.2  2002/05/28 04:07:28  theoddone33
30  * New graphics stubbing arrangement
31  *
32  * Revision 1.1.1.1  2002/05/03 03:28:09  root
33  * Initial import.
34  *
35  * 
36  * 12    7/14/99 9:42a Dave
37  * Put in clear_color debug function. Put in base for 3dnow stuff / P3
38  * stuff
39  * 
40  * 11    7/09/99 9:51a Dave
41  * Added thick polyline code.
42  * 
43  * 10    6/29/99 10:35a Dave
44  * Interface polygon bitmaps! Whee!
45  * 
46  * 9     2/03/99 11:44a Dave
47  * Fixed d3d transparent textures.
48  * 
49  * 8     1/24/99 11:37p Dave
50  * First full rev of beam weapons. Very customizable. Removed some bogus
51  * Int3()'s in low level net code.
52  * 
53  * 7     12/18/98 1:13a Dave
54  * Rough 1024x768 support for Direct3D. Proper detection and usage through
55  * the launcher.
56  * 
57  * 6     12/06/98 2:36p Dave
58  * Drastically improved nebula fogging.
59  * 
60  * 5     11/30/98 5:31p Dave
61  * Fixed up Fred support for software mode.
62  * 
63  * 4     11/30/98 1:07p Dave
64  * 16 bit conversion, first run.
65  * 
66  * 3     11/11/98 5:37p Dave
67  * Checkin for multiplayer testing.
68  * 
69  * 2     10/07/98 10:53a Dave
70  * Initial checkin.
71  * 
72  * 1     10/07/98 10:49a Dave
73  * 
74  * 84    5/20/98 9:46p John
75  * added code so the places in code that change half the palette don't
76  * have to clear the screen.
77  * 
78  * 83    5/18/98 11:17a John
79  * Fixed some bugs with software window and output window.
80  * 
81  * 82    5/17/98 5:03p John
82  * Fixed some bugs that make the taskbar interfere with the DEBUG-only
83  * mouse cursor.
84  * 
85  * 81    5/15/98 2:44p John
86  * Made windowed mode not touch the window if no handle to window found.
87  * 
88  * 80    5/14/98 5:42p John
89  * Revamped the whole window position/mouse code for the graphics windows.
90  * 
91  * 79    5/07/98 6:58p Hoffoss
92  * Made changes to mouse code to fix a number of problems.
93  * 
94  * 78    5/06/98 5:30p John
95  * Removed unused cfilearchiver.  Removed/replaced some unused/little used
96  * graphics functions, namely gradient_h and _v and pixel_sp.   Put in new
97  * DirectX header files and libs that fixed the Direct3D alpha blending
98  * problems.
99  * 
100  * 77    4/23/98 8:24a John
101  * Changed the way palette effect works so that:
102  * 1. If gr_flash isn't called this frame, screen shows no flash.
103  * 2. With hardware, only 3d portion of screen gets flashed.
104  * 
105  * 76    4/21/98 5:22p John
106  * Fixed all the &^#@$ cursor bugs with popups.   For Glide, had problem
107  * with mouse restoring assuming back buffer was same buffer last frame,
108  * for software, problems with half drawn new frames, then mouse got
109  * restored on top of that with old data.
110  * 
111  * 75    4/14/98 12:15p John
112  * Made 16-bpp movies work.
113  * 
114  * 74    4/11/98 12:48p John
115  * Made software fade out actually clear screen to black.
116  * 
117  * 73    4/01/98 9:21p John
118  * Made NDEBUG, optimized build with no warnings or errors.
119  * 
120  * 72    3/25/98 8:07p John
121  * Restructured software rendering into two modules; One for windowed
122  * debug mode and one for DirectX fullscreen.   
123  * 
124  * 71    3/24/98 3:58p John
125  * Put in (hopefully) final gamma setting code.
126  * 
127  * 70    3/17/98 5:55p John
128  * Added code to dump Glide frames.   Moved Allender's  "hack" code out of
129  * Freespace.cpp into the proper place, graphics lib.
130  * 
131  * 69    3/14/98 5:46p John
132  * 
133  * 68    3/14/98 5:43p John
134  * Saved area under mouse cursor.  Made save_Screen restore it so no mouse
135  * is on those screens.
136  * 
137  * 67    3/12/98 8:50a John
138  * took out "allocating zbuffer" mprintf
139  * 
140  * 66    3/10/98 4:18p John
141  * Cleaned up graphics lib.  Took out most unused gr functions.   Made D3D
142  * & Glide have popups and print screen.  Took out all >8bpp software
143  * support.  Made Fred zbuffer.  Made zbuffer allocate dynamically to
144  * support Fred.  Made zbuffering key off of functions rather than one
145  * global variable.
146  * 
147  * 65    3/06/98 4:09p John
148  * Changed the way we do bitmap RLE'ing... this speds up HUD bitmaps by
149  * about 2x
150  * 
151  * 64    3/05/98 4:28p John
152  * Made mouse cursor hide while setting palette.
153  * 
154  * 63    3/02/98 5:42p John
155  * Removed WinAVI stuff from Freespace.  Made all HUD gauges wriggle from
156  * afterburner.  Made gr_set_clip work good with negative x &y.  Made
157  * model_caching be on by default.  Made each cached model have it's own
158  * bitmap id.  Made asteroids not rotate when model_caching is on.  
159  * 
160  * 62    2/24/98 3:50p John
161  * Made Alt+Tabbing work with Glide.  Made Int3's restore Glide screen on
162  * next flip.
163  * 
164  * 61    2/24/98 1:59p John
165  * Made gr_aabitmap_ex clip properly
166  * 
167  * 60    2/05/98 9:21p John
168  * Some new Direct3D code.   Added code to monitor a ton of stuff in the
169  * game.
170  * 
171  * 59    1/19/98 6:15p John
172  * Fixed all my Optimized Build compiler warnings
173  * 
174  * 58    1/15/98 4:07p John
175  * added grx_set_palette_internal which doesn't clear the screen and flip
176  * before setting the palette.
177  * 
178  * 57    1/15/98 4:02p John
179  * Fixed stupid bug on my part where I called graphics rendering functions
180  * from another thread!!! Duh!  Fixed it by setting a variable and then
181  * resetting the palette at the next flip.
182  * 
183  * 56    1/08/98 5:06p John
184  * Replaced Int3 with a mprintf
185  * 
186  * 55    1/08/98 3:10p John
187  * Undid my palette changes for interplay rev
188  * 
189  * 54    1/08/98 3:03p John
190  * 
191  * 53    1/05/98 11:52a Jasen
192  * JAS: Made graphics code work if os_get_window returns NULL when trying
193  * to go fullscreen.
194  * 
195  * 52    12/30/97 6:47p John
196  * Made fade time correct
197  * 
198  * 51    12/30/97 6:46p John
199  * Added first rev of palette fade in out functions
200  * 
201  * 50    12/21/97 4:33p John
202  * Made debug console functions a class that registers itself
203  * automatically, so you don't need to add the function to
204  * debugfunctions.cpp.  
205  * 
206  * 49    12/05/97 4:26p John
207  * made palette set code be like it was before.
208  * 
209  * 48    12/04/97 8:05p John
210  * added test code to brighten palette
211  * 
212  * 47    12/03/97 2:16p John
213  * Took out blur code and fake_vram buffer.
214  * 
215  * 46    12/02/97 4:00p John
216  * Added first rev of thruster glow, along with variable levels of
217  * translucency, which retquired some restructing of palman.
218  * 
219  * 45    11/30/97 12:18p John
220  * added more 24 & 32-bpp primitives
221  * 
222  * 44    11/29/97 2:06p John
223  * added mode 16-bpp support
224  * 
225  * 43    11/21/97 11:32a John
226  * Added nebulas.   Fixed some warpout bugs.
227  * 
228  * 42    11/20/97 9:51a John
229  * added code to force screen to 16-bit even if rendering 8.
230  * 
231  * 41    11/17/97 10:33a John
232  * updated force_windowed code.
233  * 
234  * 40    11/14/97 3:54p John
235  * Added triple buffering.
236  * 
237  * 39    11/14/97 3:08p Johnson
238  * fixed bug with fred.
239  * 
240  * 38    11/14/97 12:30p John
241  * Fixed some DirectX bugs.  Moved the 8-16 xlat tables into Graphics
242  * libs.  Made 16-bpp DirectX modes know what bitmap format they're in.
243  * 
244  * 37    11/06/97 4:12p John
245  * Took out debug code that returned NULL for DD.
246  * 
247  * 36    11/05/97 12:35p John
248  * added correct gr_bitmap_ex that does clipping
249  * 
250  * 
251  * 35    10/31/97 9:43a John
252  * fixed red background color bug.
253  * 
254  * 34    10/19/97 1:53p John
255  * hacked in a fix to a zbuffer error caused by inaccuracies.
256  * 
257  * 33    10/19/97 12:55p John
258  * new code to lock / unlock surfaces for smooth directx integration.
259  * 
260  * 32    10/15/97 4:48p John
261  * added 16-bpp aascaler
262  * 
263  * 31    10/14/97 4:50p John
264  * more 16 bpp stuff.
265  * 
266  * 30    10/14/97 8:08a John
267  * added a bunch more 16 bit support
268  * 
269  * 29    10/09/97 5:23p John
270  * Added support for more 16-bpp functions
271  * 
272  * 28    10/03/97 9:10a John
273  * added better antialiased line drawer
274  * 
275  * 27    9/23/97 11:50a Lawrance
276  * jas: added xparency to rle'd bitblts
277  * 
278  * 26    9/23/97 10:45a John
279  * made so you can tell bitblt code to rle a bitmap by passing flag to
280  * gr_set_bitmap
281  * 
282  * 25    9/20/97 10:48a John
283  * added motion blur code.
284  * 
285  * 24    9/09/97 10:39a Sandeep
286  * fixed warning level 4
287  * 
288  * 23    9/07/97 2:34p John
289  * fixed bug with fullscreen toggling not working when in > 8bpp mode.
290  * 
291  * 22    9/03/97 4:32p John
292  * changed bmpman to only accept ani and pcx's.  made passing .pcx or .ani
293  * to bm_load functions not needed.   Made bmpman keep track of palettes
294  * for bitmaps not mapped into game palettes.
295  * 
296  * 21    8/26/97 11:30a John
297  * removed call to restoredisplymode when going from fullscreen to
298  * windowed, which hopefully fixes the screwey taskbar problem.
299  * 
300  * 20    8/04/97 4:47p John
301  * added gr_aascaler.
302  * 
303  * 19    7/17/97 9:31a John
304  * made all directX header files name start with a v
305  * 
306  * 18    7/10/97 2:06p John
307  * added code to specify alphablending type for bitmaps.
308  * 
309  * 17    6/20/97 1:50p John
310  * added rle code to bmpman.  made gr8_aabitmap use it.
311  * 
312  * 16    6/17/97 12:03p John
313  * Moved color/alphacolor functions into their own module.  Made all color
314  * functions be part of the low-level graphics drivers, not just the
315  * grsoft.
316  * 
317  * 15    6/13/97 5:35p John
318  * added some antialiased bitmaps and lines
319  * 
320  * 14    6/12/97 2:50a Lawrance
321  * bm_unlock() now passed bitmap number, not pointer
322  * 
323  * 13    6/11/97 5:49p John
324  * Changed palette code to only recalculate alphacolors when needed, not
325  * when palette changes.
326  * 
327  * 12    6/11/97 1:12p John
328  * Started fixing all the text colors in the game.
329  * 
330  * 11    6/06/97 5:03p John
331  * fixed bug withalpha colors failing after gr_init
332  * 
333  * 10    6/06/97 4:41p John
334  * Fixed alpha colors to be smoothly integrated into gr_set_color_fast
335  * code.
336  * 
337  * 9     5/29/97 3:09p John
338  * Took out debug menu.  
339  * Made software scaler draw larger bitmaps.
340  * Optimized Direct3D some.
341  * 
342  * 8     5/16/97 9:11a John
343  * fixed bug that made Ctrl+Break in fullscreen hang
344  * 
345  * 7     5/14/97 4:40p John
346  * took out mprintf
347  * 
348  * 6     5/14/97 4:38p John
349  * Fixed print_screen bug.
350  * 
351  * 5     5/14/97 2:09p John
352  * made fullscreen use 254 palette entries.    Used
353  * CreateCompatibleDC(NULL) Instead of GetDC(HWND_DESKTOP) because the
354  * GetDC method fails when you change screen depth dynamically under NT.
355  * 
356  * 4     5/14/97 10:53a John
357  * fixed some discrepencies between d3d and software palette setting.
358  * 
359  * 3     5/14/97 8:53a John
360  * Fixed a palette bug when switching into/outof d3d mode.
361  * 
362  * 2     5/13/97 12:39p John
363  * Got fullscreen mode working.
364  * 
365  * 1     5/12/97 12:14p John
366  *
367  * $NoKeywords: $
368  */
369
370 #include <math.h>
371 #ifndef PLAT_UNIX
372 #include <windows.h>
373 #include <windowsx.h>
374 #endif
375
376 #include "osapi.h"
377 #include "2d.h"
378 #include "bmpman.h"
379 #include "key.h"
380 #include "floating.h"
381 #include "palman.h"
382 #include "grsoft.h"
383 #include "grinternal.h"
384
385 // Headers for 2d functions
386 #include "pixel.h"
387 #include "line.h"
388 #include "scaler.h"
389 #include "tmapper.h"
390 #include "circle.h"
391 #include "shade.h"
392 #include "rect.h"
393 #include "gradient.h"
394 #include "pcxutils.h"
395 #include "osapi.h"
396 #include "mouse.h"
397 #include "font.h"
398 #include "timer.h"
399 #include "colors.h"
400 #include "bitblt.h"
401
402  // Window's specific
403
404 // This structure is the same as LOGPALETTE except that LOGPALETTE
405 // requires you to malloc out space for the palette, which just isn't
406 // worth the trouble.
407
408 #ifdef PLAT_UNIX
409 SDL_Surface *soft_surface;
410 #endif
411
412 #ifndef PLAT_UNIX
413 typedef struct {
414     WORD         palVersion; 
415     WORD         palNumEntries; 
416     PALETTEENTRY palPalEntry[256]; 
417 } EZ_LOGPALETTE; 
418
419 // This structure is the same as BITMAPINFO except that BITMAPINFO
420 // requires you to malloc out space for the palette, which just isn't
421 // worth the trouble.   I also went ahead and threw a handy union to
422 // easily reference the hicolor masks in 15/16 bpp modes.
423 typedef struct  {
424         BITMAPINFOHEADER Header;
425         union {
426                 RGBQUAD aColors[256];
427                 ushort PalIndex[256];
428                 uint hicolor_masks[3];
429         } Colors;
430 } EZ_BITMAPINFO;
431
432 EZ_BITMAPINFO DibInfo;
433 HBITMAP hDibSection = NULL;
434 HBITMAP hOldBitmap = NULL;
435 HDC hDibDC = NULL;
436 void *lpDibBits=NULL;
437
438 HPALETTE hOldPalette=NULL, hPalette = NULL;     
439 #endif
440
441 int Gr_soft_inited = 0;
442
443 static volatile int Grsoft_activated = 1;                               // If set, that means application got focus, so reset palette
444
445 void gr_buffer_release()
446 {
447 #ifndef PLAT_UNIX
448         if ( hPalette ) {
449                 if (hDibDC)
450                         SelectPalette( hDibDC, hOldPalette, FALSE );
451                 if (!DeleteObject(hPalette))    {
452                         mprintf(( "JOHN: Couldn't delete palette object\n" ));
453                 }
454                 hPalette = NULL;
455         }
456
457         if ( hDibDC )   {
458                 SelectObject(hDibDC, hOldBitmap );
459                 DeleteDC(hDibDC);
460                 hDibDC = NULL;
461         }
462
463         if ( hDibSection )      {
464                 DeleteObject(hDibSection);
465                 hDibSection = NULL;
466         }
467 #endif
468 }
469
470
471 void gr_buffer_create( int w, int h, int bpp )
472 {
473         if (w & 3) {
474                 Int3(); // w must be multiple 4
475                 return;
476         }
477
478         gr_buffer_release();
479
480 #ifndef PLAT_UNIX
481         memset( &DibInfo, 0, sizeof(EZ_BITMAPINFO));
482    DibInfo.Header.biSize = sizeof(BITMAPINFOHEADER);
483         DibInfo.Header.biWidth = w;
484         DibInfo.Header.biHeight = h;
485         DibInfo.Header.biPlanes = 1; 
486         DibInfo.Header.biClrUsed = 0;
487 #endif
488
489         switch( bpp )   {
490         case 8:
491                 Gr_red.bits = 8;
492                 Gr_red.shift = 16;
493                 Gr_red.scale = 1;
494                 Gr_red.mask = 0xff0000;
495
496                 Gr_green.bits = 8;
497                 Gr_green.shift = 8;
498                 Gr_green.scale = 1;
499                 Gr_green.mask = 0xff00;
500
501                 Gr_blue.bits = 8;
502                 Gr_blue.shift = 0;
503                 Gr_blue.scale = 1;
504                 Gr_blue.mask = 0xff;
505
506 #ifndef PLAT_UNIX
507                 DibInfo.Header.biCompression = BI_RGB; 
508                 DibInfo.Header.biBitCount = 8; 
509                 for (int i=0; i<256; i++ )      {
510                         DibInfo.Colors.aColors[i].rgbRed = 0;
511                         DibInfo.Colors.aColors[i].rgbGreen = 0;
512                         DibInfo.Colors.aColors[i].rgbBlue = 0;
513                         DibInfo.Colors.aColors[i].rgbReserved = 0;
514                 }
515                 break;
516 #endif
517
518         case 15:
519                 Gr_red.bits = 5;
520                 Gr_red.shift = 10;
521                 Gr_red.scale = 8;
522                 Gr_red.mask = 0x7C00;
523
524                 Gr_green.bits = 5;
525                 Gr_green.shift = 5;
526                 Gr_green.scale = 8;
527                 Gr_green.mask = 0x3E0;
528
529                 Gr_blue.bits = 5;
530                 Gr_blue.shift = 0;
531                 Gr_blue.scale = 8;
532                 Gr_blue.mask = 0x1F;
533
534 #ifndef PLAT_UNIX
535                 DibInfo.Header.biCompression = BI_BITFIELDS;
536                 DibInfo.Header.biBitCount = 16; 
537                 DibInfo.Colors.hicolor_masks[0] = Gr_red.mask;
538                 DibInfo.Colors.hicolor_masks[1] = Gr_green.mask;
539                 DibInfo.Colors.hicolor_masks[2] = Gr_blue.mask;
540 #endif
541
542
543                 break;
544
545         case 16:
546                 Gr_red.bits = 5;
547                 Gr_red.shift = 11;
548                 Gr_red.scale = 8;
549                 Gr_red.mask = 0xF800;
550
551                 Gr_green.bits = 6;
552                 Gr_green.shift = 5;
553                 Gr_green.scale = 4;
554                 Gr_green.mask = 0x7E0;
555
556                 Gr_blue.bits = 5;
557                 Gr_blue.shift = 0;
558                 Gr_blue.scale = 8;
559                 Gr_blue.mask = 0x1F;
560
561 #ifndef PLAT_UNIX
562                 DibInfo.Header.biCompression = BI_BITFIELDS;
563                 DibInfo.Header.biBitCount = 16; 
564                 DibInfo.Colors.hicolor_masks[0] = Gr_red.mask;
565                 DibInfo.Colors.hicolor_masks[1] = Gr_green.mask;
566                 DibInfo.Colors.hicolor_masks[2] = Gr_blue.mask;
567 #endif
568                 break;
569
570         case 24:
571         case 32:
572                 Gr_red.bits = 8;
573                 Gr_red.shift = 16;
574                 Gr_red.scale = 1;
575                 Gr_red.mask = 0xff0000;
576
577                 Gr_green.bits = 8;
578                 Gr_green.shift = 8;
579                 Gr_green.scale = 1;
580                 Gr_green.mask = 0xff00;
581
582                 Gr_blue.bits = 8;
583                 Gr_blue.shift = 0;
584                 Gr_blue.scale = 1;
585                 Gr_blue.mask = 0xff;
586
587 #ifndef PLAT_UNIX
588                 DibInfo.Header.biCompression = BI_RGB; 
589                 DibInfo.Header.biBitCount = unsigned short(bpp); 
590 #endif
591                 break;
592
593         default:
594                 Int3(); // Illegal bpp
595         }
596
597 #ifndef PLAT_UNIX
598         lpDibBits = NULL;
599
600         hDibDC = CreateCompatibleDC(NULL);
601         hDibSection = CreateDIBSection(hDibDC,(BITMAPINFO *)&DibInfo,DIB_RGB_COLORS,&lpDibBits,NULL,NULL);
602         hOldBitmap = (HBITMAP)SelectObject(hDibDC, hDibSection );
603
604         if ( hDibSection == NULL )      {
605                 Int3(); // couldn't allocate dib section
606         }
607 #endif
608 }
609
610
611 // This makes a best-fit palette from the 256 target colors and
612 // the system colors which should look better than only using the
613 // colors in the range 10-246, but it will totally reorder the palette.
614 // All colors get changed in target_palette.
615
616
617 #ifndef PLAT_UNIX
618 HPALETTE gr_create_palette_0(ubyte * target_palette)
619 {
620         EZ_LOGPALETTE LogicalPalette;
621         HDC ScreenDC;
622         HPALETTE hpal,hpalOld;
623         PALETTEENTRY pe[256];
624         int NumSysColors, NumColors;
625         int i;
626
627         // Create a 1-1 mapping of the system palette
628         LogicalPalette.palVersion = 0x300;
629         LogicalPalette.palNumEntries = 256;
630
631         // Pack in all the colors
632         for (i=0;i<256; i++)    {
633                 LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0];
634                 LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1];
635                 LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2];
636                 LogicalPalette.palPalEntry[i].peFlags = 0;      //PC_EXPLICIT;
637         } 
638
639         hpal = CreatePalette( (LOGPALETTE *)&LogicalPalette );
640
641         ScreenDC = CreateCompatibleDC(NULL);
642
643         if ( !(GetDeviceCaps(ScreenDC,RASTERCAPS) & RC_PALETTE) ) {
644                 DeleteDC(ScreenDC);
645                 return hpal;
646         }
647          
648         NumSysColors = GetDeviceCaps( ScreenDC, NUMCOLORS );
649         NumColors = GetDeviceCaps( ScreenDC, SIZEPALETTE );
650
651         // Reset all the Palette Manager tables
652         SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC );
653         SetSystemPaletteUse( ScreenDC, SYSPAL_STATIC );
654
655         // Enter our palette's values into the free slots
656         hpalOld=SelectPalette( ScreenDC, hpal, FALSE );
657         RealizePalette( ScreenDC );
658         SelectPalette( ScreenDC, hpalOld, FALSE );
659
660         GetSystemPaletteEntries(ScreenDC,0,NumColors,pe);
661
662         for (i=0; i<NumSysColors/2; i++ )       {
663                 pe[i].peFlags = 0;
664         }
665         for (; i<NumColors - NumSysColors/2; i++ )      {
666                 pe[i].peFlags = PC_NOCOLLAPSE;
667         }
668         for (; i<NumColors; i++ )       {
669                 pe[i].peFlags = 0;
670         }
671         ResizePalette( hpal, NumColors);
672         SetPaletteEntries( hpal, 0, NumColors, pe );
673                 
674         for (i=0; i<256; i++ )  {
675                 target_palette[i*3+0] = pe[i].peRed;
676                 target_palette[i*3+1] = pe[i].peGreen;
677                 target_palette[i*3+2] = pe[i].peBlue;
678         }
679
680         DeleteDC(ScreenDC);
681
682         return hpal;
683 }
684
685 HPALETTE gr_create_palette_256( ubyte * target_palette )
686 {
687         EZ_LOGPALETTE LogicalPalette;
688         int i;
689
690         // Pack in all the colors
691         for (i=0;i<256; i++)    {
692                 LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0];
693                 LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1];
694                 LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2];
695                 LogicalPalette.palPalEntry[i].peFlags = 0;      //PC_RESERVED;  //PC_EXPLICIT;
696         } 
697
698         // Create a 1-1 mapping of the system palette
699         LogicalPalette.palVersion = 0x300;
700         LogicalPalette.palNumEntries = 256;
701
702         return CreatePalette( (LOGPALETTE *)&LogicalPalette );
703 }
704
705 // This makes an indentity logical palette that saves entries 10-246
706 // and leaves them in place.  Colors 0-9 and 246-255 get changed in 
707 // target_palette.  trash_flag tells us whether to trash the system palette
708 // or not
709 HPALETTE gr_create_palette_236( ubyte * target_palette )
710 {
711         EZ_LOGPALETTE LogicalPalette;
712         HDC ScreenDC;
713         int NumSysColors, NumColors, UserLowest, UserHighest;
714         int i;
715
716         // Pack in all the colors
717         for (i=0;i<256; i++)    {
718                 LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0];
719                 LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1];
720                 LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2];
721                 LogicalPalette.palPalEntry[i].peFlags = 0;      //PC_EXPLICIT;
722         } 
723
724         // Create a 1-1 mapping of the system palette
725         LogicalPalette.palVersion = 0x300;
726         LogicalPalette.palNumEntries = 256;
727
728         ScreenDC = CreateCompatibleDC(NULL);
729
730         // Reset all the Palette Manager tables
731         SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC );
732         SetSystemPaletteUse( ScreenDC, SYSPAL_STATIC );
733                 
734         if ( !(GetDeviceCaps(ScreenDC,RASTERCAPS) & RC_PALETTE) ) {
735                 DeleteDC(ScreenDC);
736                 return CreatePalette( (LOGPALETTE *)&LogicalPalette );
737         }
738
739         NumSysColors = GetDeviceCaps( ScreenDC, NUMCOLORS );
740         NumColors = GetDeviceCaps( ScreenDC, SIZEPALETTE );
741
742         Assert( NumColors <= 256 );
743
744         UserLowest = NumSysColors/2;                                                            // 10 normally
745         UserHighest = NumColors - NumSysColors/2 - 1;           // 245 normally
746
747         Assert( (UserHighest - UserLowest + 1) >= 236 );
748                         
749         GetSystemPaletteEntries(ScreenDC,0,NumSysColors/2,LogicalPalette.palPalEntry);
750         GetSystemPaletteEntries(ScreenDC,UserHighest+1,NumSysColors/2,LogicalPalette.palPalEntry+1+UserHighest);
751
752         DeleteDC(ScreenDC);
753                 
754         for (i=0; i<256; i++ )  {
755
756                 if ( (i >= UserLowest) && (i<=UserHighest) )    {
757                         LogicalPalette.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
758                 } else
759                         LogicalPalette.palPalEntry[i].peFlags = 0;
760
761                 target_palette[i*3+0] = LogicalPalette.palPalEntry[i].peRed;
762                 target_palette[i*3+1] = LogicalPalette.palPalEntry[i].peGreen;
763                 target_palette[i*3+2] = LogicalPalette.palPalEntry[i].peBlue;
764         }
765
766         return CreatePalette( (LOGPALETTE *)&LogicalPalette );
767 }
768
769 HPALETTE gr_create_palette_254( ubyte * target_palette )
770 {
771         EZ_LOGPALETTE LogicalPalette;
772         HDC ScreenDC;
773         int NumSysColors, NumColors, UserLowest, UserHighest;
774         int i;
775
776         // Pack in all the colors
777         for (i=0;i<256; i++)    {
778                 LogicalPalette.palPalEntry[i].peRed = target_palette[i*3+0];
779                 LogicalPalette.palPalEntry[i].peGreen = target_palette[i*3+1];
780                 LogicalPalette.palPalEntry[i].peBlue = target_palette[i*3+2];
781                 LogicalPalette.palPalEntry[i].peFlags = 0;      //PC_EXPLICIT;
782         } 
783
784         // Create a 1-1 mapping of the system palette
785         LogicalPalette.palVersion = 0x300;
786         LogicalPalette.palNumEntries = 256;
787
788         ScreenDC = CreateCompatibleDC(NULL);
789
790         // Reset all the Palette Manager tables
791         SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC );
792         SetSystemPaletteUse( ScreenDC, SYSPAL_STATIC );
793                 
794         if ( !(GetDeviceCaps(ScreenDC,RASTERCAPS) & RC_PALETTE) ) {
795                 DeleteDC(ScreenDC);
796                 return CreatePalette( (LOGPALETTE *)&LogicalPalette );
797         }
798
799         SetSystemPaletteUse( ScreenDC, SYSPAL_NOSTATIC );
800         NumSysColors = 2;
801         NumColors = GetDeviceCaps( ScreenDC, SIZEPALETTE );
802
803         Assert( NumColors <= 256 );
804
805         UserLowest = NumSysColors/2;                                                            // 10 normally
806         UserHighest = NumColors - NumSysColors/2 - 1;           // 245 normally
807
808         Assert( (UserHighest - UserLowest + 1) >= 236 );
809                         
810         GetSystemPaletteEntries(ScreenDC,0,NumSysColors/2,LogicalPalette.palPalEntry);
811         GetSystemPaletteEntries(ScreenDC,UserHighest+1,NumSysColors/2,LogicalPalette.palPalEntry+1+UserHighest);
812
813         DeleteDC(ScreenDC);
814         
815         for (i=0; i<256; i++ )  {
816
817                 if ( (i >= UserLowest) && (i<=UserHighest) )    {
818                         LogicalPalette.palPalEntry[i].peFlags = PC_NOCOLLAPSE;
819                 } else
820                         LogicalPalette.palPalEntry[i].peFlags = 0;
821
822                 target_palette[i*3+0] = LogicalPalette.palPalEntry[i].peRed;
823                 target_palette[i*3+1] = LogicalPalette.palPalEntry[i].peGreen;
824                 target_palette[i*3+2] = LogicalPalette.palPalEntry[i].peBlue;
825         }
826
827         return CreatePalette( (LOGPALETTE *)&LogicalPalette );
828 }
829 #endif // !PLAT_UNIX
830
831 void grx_set_palette_internal( ubyte * new_pal )
832 {
833 #ifdef PLAT_UNIX
834         SDL_Color colors[256];
835 #else
836         if ( hPalette ) {
837                 if (hDibDC)
838                         SelectPalette( hDibDC, hOldPalette, FALSE );
839                 if (!DeleteObject(hPalette))    {
840                         mprintf(( "JOHN: Couldn't delete palette object\n" ));
841                 }
842                 hPalette = NULL;
843         }
844 #endif
845
846
847         // Make sure color 0 is black
848         if ( (new_pal[0]!=0) || (new_pal[1]!=0) || (new_pal[2]!=0) )    {
849                 // color 0 isn't black!! switch it!
850                 int i;
851                 int black_index = -1;
852
853                 for (i=1; i<256; i++ )  {
854                         if ( (new_pal[i*3+0]==0) && (new_pal[i*3+1]==0) && (new_pal[i*3+2]==0) )        {       
855                                 black_index = i;
856                                 break;
857                         }
858                 }
859                 if ( black_index > -1 ) {
860                         // swap black and color 0, so color 0 is black
861                         ubyte tmp[3];
862                         tmp[0] = new_pal[black_index*3+0];
863                         tmp[1] = new_pal[black_index*3+1];
864                         tmp[2] = new_pal[black_index*3+2];
865
866                         new_pal[black_index*3+0] = new_pal[0];
867                         new_pal[black_index*3+1] = new_pal[1];
868                         new_pal[black_index*3+2] = new_pal[2];
869
870                         new_pal[0] = tmp[0];
871                         new_pal[1] = tmp[1];
872                         new_pal[2] = tmp[2];
873                 } else {
874                         // no black in palette, force color 0 to be black.
875                         new_pal[0] = 0;
876                         new_pal[1] = 0;
877                         new_pal[2] = 0;
878                 }
879         }
880
881
882
883         if ( gr_screen.bits_per_pixel==8 )      {
884
885                 // Name                    n_preserved  One-one     Speed      Windowed? 
886                 // -------------------------------------------------------------------
887                 // gr_create_palette_256   256          0-255       Slow       Yes
888                 // gr_create_palette_254   254          1-254       Fast       No
889                 // gr_create_palette_236   236          10-245      Fast       Yes
890                 // gr_create_palette_0     0            none        Fast       Yes              
891
892 /*
893                 n_preserved = 256;
894
895                 if ( n_preserved <= 0 ) {
896                         hPalette = gr_create_palette_0(new_pal);        // No colors mapped one-to-one, but probably has close to all 256 colors in it somewhere.
897                 } else if ( n_preserved <= 236 )        {
898                         hPalette = gr_create_palette_236(new_pal);      // All colors except low 10 and high 10 mapped one-to-one
899                 } else if ( n_preserved <= 254 )        {
900                         hPalette = gr_create_palette_254(new_pal);      // All colors except 0 and 255 mapped one-to-one, but changes system colors.  Not pretty in a window.
901                 } else {
902 */
903 #ifdef PLAT_UNIX
904                 for (int i = 0; i < 256; i++)
905                 {
906                         colors[i].r = new_pal[i*3+0];
907                         colors[i].g = new_pal[i*3+1];
908                         colors[i].b = new_pal[i*3+2];
909                 }
910                 SDL_SetColors (soft_surface, colors, 0, 256);
911         }
912 #else
913                 hPalette = gr_create_palette_256(new_pal);      // All 256 mapped one-to-one, but BLT's are slow.
914
915                 if ( hDibDC )   {
916                         int i; 
917                         for (i=0; i<256; i++ )  {
918                                 DibInfo.Colors.aColors[i].rgbRed = new_pal[i*3+0];
919                                 DibInfo.Colors.aColors[i].rgbGreen = new_pal[i*3+1];
920                                 DibInfo.Colors.aColors[i].rgbBlue = new_pal[i*3+2];
921                                 DibInfo.Colors.aColors[i].rgbReserved = 0;
922                         }
923
924                         hOldPalette = SelectPalette( hDibDC, hPalette, FALSE );
925                         SetDIBColorTable( hDibDC, 0, 256, DibInfo.Colors.aColors );
926                 }
927         } else {
928                 hPalette = NULL;
929         }
930 #endif
931 }
932
933
934
935 void grx_set_palette( ubyte * new_pal, int is_alphacolor )
936 {
937 #ifdef PLAT_UNIX
938         Mouse_hidden++;
939         gr_reset_clip();
940         gr_clear();
941         gr_flip();
942         Mouse_hidden--;
943
944         grx_set_palette_internal(new_pal);
945 #else
946         if ( hPalette ) {
947                 Mouse_hidden++;
948                 gr_reset_clip();
949                 gr_clear();
950                 gr_flip();
951                 Mouse_hidden--;
952         }
953
954         grx_set_palette_internal(new_pal);
955 #endif
956 }
957
958
959 void grx_print_screen(char * filename)
960 {
961         int i;
962         ubyte **row_data = (ubyte **)malloc( gr_screen.max_h * sizeof(ubyte *) );
963         if ( !row_data )        {
964                 mprintf(( "couldn't allocate enough memory to dump screen\n" ));
965                 return;
966         }
967
968         gr_lock();
969
970         for (i=0; i<gr_screen.max_h; i++ )      {
971                 row_data[i] = GR_SCREEN_PTR(ubyte,0,i);
972         }
973
974         pcx_write_bitmap( filename, gr_screen.max_w, gr_screen.max_h, row_data, Gr_current_palette );
975
976         gr_unlock();
977
978         free(row_data);
979 }
980
981
982 uint gr_soft_lock()
983 {
984         return 1;
985 }
986
987 void gr_soft_unlock()
988 {
989 }
990
991
992 void grx_set_palette_internal( ubyte * new_pal );
993
994 int Grx_mouse_saved = 0;
995 int Grx_mouse_saved_x1 = 0;
996 int Grx_mouse_saved_y1 = 0;
997 int Grx_mouse_saved_x2 = 0;
998 int Grx_mouse_saved_y2 = 0;
999 int Grx_mouse_saved_w = 0;
1000 int Grx_mouse_saved_h = 0;
1001 #define MAX_SAVE_SIZE (32*32)
1002 ubyte Grx_mouse_saved_data[MAX_SAVE_SIZE];
1003
1004 // Clamps X between R1 and R2
1005 #define CLAMP(x,r1,r2) do { if ( (x) < (r1) ) (x) = (r1); else if ((x) > (r2)) (x) = (r2); } while(0)
1006
1007 void grx_save_mouse_area(int x, int y, int w, int h )
1008 {
1009         Grx_mouse_saved_x1 = x; 
1010         Grx_mouse_saved_y1 = y;
1011         Grx_mouse_saved_x2 = x+w-1;
1012         Grx_mouse_saved_y2 = y+h-1;
1013          
1014         CLAMP(Grx_mouse_saved_x1, gr_screen.clip_left, gr_screen.clip_right );
1015         CLAMP(Grx_mouse_saved_x2, gr_screen.clip_left, gr_screen.clip_right );
1016         CLAMP(Grx_mouse_saved_y1, gr_screen.clip_top, gr_screen.clip_bottom );
1017         CLAMP(Grx_mouse_saved_y2, gr_screen.clip_top, gr_screen.clip_bottom );
1018
1019         Grx_mouse_saved_w = Grx_mouse_saved_x2 - Grx_mouse_saved_x1 + 1;
1020         Grx_mouse_saved_h = Grx_mouse_saved_y2 - Grx_mouse_saved_y1 + 1;
1021
1022         if ( Grx_mouse_saved_w < 1 ) return;
1023         if ( Grx_mouse_saved_h < 1 ) return;
1024
1025         // Make sure we're not saving too much!
1026         Assert( (Grx_mouse_saved_w*Grx_mouse_saved_h) <= MAX_SAVE_SIZE );
1027
1028         Grx_mouse_saved = 1;
1029
1030         gr_lock();
1031
1032         ubyte *sptr, *dptr;
1033
1034         dptr = Grx_mouse_saved_data;
1035
1036         for (int i=0; i<Grx_mouse_saved_h; i++ )        {
1037                 sptr = GR_SCREEN_PTR(ubyte,Grx_mouse_saved_x1,Grx_mouse_saved_y1+i);
1038
1039                 for(int j=0; j<Grx_mouse_saved_w; j++ ) {
1040                         *dptr++ = *sptr++;
1041                 }
1042         }
1043
1044         gr_unlock();
1045 }
1046
1047
1048 void grx_restore_mouse_area()
1049 {
1050         if ( !Grx_mouse_saved ) {
1051                 return;
1052         }
1053
1054         gr_lock();
1055
1056         ubyte *sptr, *dptr;
1057
1058         sptr = Grx_mouse_saved_data;
1059
1060         for (int i=0; i<Grx_mouse_saved_h; i++ )        {
1061                 dptr = GR_SCREEN_PTR(ubyte,Grx_mouse_saved_x1,Grx_mouse_saved_y1+i);
1062
1063                 for(int j=0; j<Grx_mouse_saved_w; j++ ) {
1064                         *dptr++ = *sptr++;
1065                 }
1066         }
1067
1068         gr_unlock();
1069 }
1070
1071
1072 void gr_soft_activate(int active)
1073 {
1074         if ( active  )  {
1075                 Grsoft_activated++;
1076         }
1077 }
1078
1079
1080
1081 static int Palette_flashed = 0;
1082 static int Palette_flashed_last_frame = 0;
1083
1084 void grx_change_palette( ubyte *pal );
1085
1086 void grx_flip()
1087 {
1088         if ( (!Palette_flashed) && (Palette_flashed_last_frame) )       {
1089                 // Reset flash
1090                 grx_change_palette( gr_palette );
1091         }
1092
1093         Palette_flashed_last_frame = Palette_flashed;
1094         Palette_flashed = 0;
1095
1096         // If program reactivated, flip set new palette.
1097         // We do the cnt temporary variable because Grsoft_activated
1098         // can be set during interrupts.
1099
1100         int cnt = Grsoft_activated;
1101         if ( cnt )      {
1102                 Grsoft_activated -= cnt;
1103
1104                 ubyte new_pal[768];
1105                 memcpy( new_pal, gr_palette, 768 );
1106                 grx_set_palette_internal( new_pal );            // Call internal one so it doesn't clear screen and call flip
1107         }
1108
1109         gr_reset_clip();
1110
1111 //      if (0) {
1112 //              int i;
1113 //              for (i=0; i<gr_screen.max_h; i++ )      {
1114 //                      memset( gr_screen.row_data[i], i & 255, abs(gr_screen.rowsize) );
1115 //              }
1116 //      }
1117
1118         int mx, my;
1119
1120         Grx_mouse_saved = 0;            // assume not saved
1121
1122         mouse_eval_deltas();
1123         if ( mouse_is_visible() )       {
1124                 gr_reset_clip();
1125                 mouse_get_pos( &mx, &my );
1126                 grx_save_mouse_area(mx,my,32,32);
1127                 if ( Gr_cursor == -1 )  {
1128                         gr_set_color(255,255,255);
1129                         gr_line( mx, my, mx+7, my + 7 );
1130                         gr_line( mx, my, mx+5, my );
1131                         gr_line( mx, my, mx, my+5 );
1132                 } else {
1133                         gr_set_bitmap(Gr_cursor);
1134                         gr_bitmap( mx, my );
1135                 }
1136         } 
1137
1138         fix t1, t2, d, t;
1139
1140 #ifdef PLAT_UNIX
1141         int x = gr_screen.offset_x;
1142         int y = gr_screen.offset_y;
1143         int w = gr_screen.clip_width;
1144         int h = gr_screen.clip_height;
1145
1146         t1 = timer_get_fixed_seconds();
1147
1148         SDL_UpdateRect (soft_surface, x, y, w, h);
1149
1150         t2 = timer_get_fixed_seconds();
1151         d = t2 - t1;
1152         t = (w*h*gr_screen.bytes_per_pixel)/1024;
1153 #else
1154         HWND hwnd = (HWND)os_get_window();
1155
1156         if ( hwnd )     {
1157                 int x = gr_screen.offset_x;
1158                 int y = gr_screen.offset_y;
1159                 int w = gr_screen.clip_width;
1160                 int h = gr_screen.clip_height;
1161
1162                 HPALETTE hOldPalette = NULL;
1163                 HDC hdc = GetDC(hwnd);
1164
1165                 if ( hdc )      {
1166                         t1 = timer_get_fixed_seconds();
1167
1168                         if (hPalette)   {
1169                                 hOldPalette=SelectPalette(hdc,hPalette, FALSE );
1170                                 uint nColors = RealizePalette( hdc );
1171                                 nColors;
1172                                 //if (nColors)  mprintf(( "Actually set %d palette colors.\n", nColors ));
1173                         }
1174
1175 #if 0 
1176                         BitBlt(hdc,0,h/2,w,h/2,hDibDC,x,y+h/2,SRCCOPY);
1177 #else
1178                         BitBlt(hdc,0,0,w,h,hDibDC,x,y,SRCCOPY);
1179 #endif
1180
1181                         if ( hOldPalette )      
1182                                 SelectPalette(hdc,hOldPalette, FALSE );
1183
1184                         ReleaseDC( hwnd, hdc );
1185
1186                         t2 = timer_get_fixed_seconds();
1187                         d = t2 - t1;
1188                         t = (w*h*gr_screen.bytes_per_pixel)/1024;
1189                         //mprintf(( "%d MB/s\n", fixmuldiv(t,65,d) ));
1190
1191                 }
1192         }
1193 #endif
1194
1195         if ( Grx_mouse_saved )  {
1196                 grx_restore_mouse_area();
1197         }
1198 }
1199
1200
1201 // switch onscreen, offscreen
1202 // Set msg to 0 if calling outside of the window handler.
1203 void grx_flip_window(uint _hdc, int x, int y, int w, int h )
1204 {
1205 #ifdef PLAT_UNIX
1206         STUB_FUNCTION;
1207 #else
1208         HDC hdc = (HDC)_hdc;
1209         HPALETTE hOldPalette = NULL;
1210         int min_w, min_h;
1211
1212         if (hPalette)   {
1213                 hOldPalette=SelectPalette(hdc,hPalette, FALSE );
1214                 RealizePalette( hdc );
1215         }
1216
1217         min_w = gr_screen.clip_width;
1218         if ( w < min_w ) min_w = w;
1219
1220         min_h = gr_screen.clip_height;
1221         if ( h < min_h ) min_h = h;
1222
1223         BitBlt(hdc,x,y,min_w,min_h,hDibDC,gr_screen.offset_x,gr_screen.offset_y,SRCCOPY);
1224
1225         //StretchBlt( hdc, 0, 0, w, h, hDibDC, 0, 0, 640, 480, SRCCOPY );
1226         
1227         if ( hOldPalette ){     
1228                 SelectPalette(hdc,hOldPalette, FALSE );
1229         }
1230 #endif
1231 }
1232
1233
1234 // sets the clipping region & offset
1235 void grx_set_clip(int x,int y,int w,int h)
1236 {
1237         gr_screen.offset_x = x;
1238         gr_screen.offset_y = y;
1239
1240         gr_screen.clip_left = 0;
1241         gr_screen.clip_right = w-1;
1242
1243         gr_screen.clip_top = 0;
1244         gr_screen.clip_bottom = h-1;
1245
1246         // check for sanity of parameters
1247         if ( gr_screen.clip_left+x < 0 ) {
1248                 gr_screen.clip_left = -x;
1249         } else if ( gr_screen.clip_left+x > gr_screen.max_w-1 ) {
1250                 gr_screen.clip_left = gr_screen.max_w-1-x;
1251         }
1252         if ( gr_screen.clip_right+x < 0 ) {
1253                 gr_screen.clip_right = -x;
1254         } else if ( gr_screen.clip_right+x >= gr_screen.max_w-1 )       {
1255                 gr_screen.clip_right = gr_screen.max_w-1-x;
1256         }
1257
1258         if ( gr_screen.clip_top+y < 0 ) {
1259                 gr_screen.clip_top = -y;
1260         } else if ( gr_screen.clip_top+y > gr_screen.max_h-1 )  {
1261                 gr_screen.clip_top = gr_screen.max_h-1-y;
1262         }
1263
1264         if ( gr_screen.clip_bottom+y < 0 ) {
1265                 gr_screen.clip_bottom = -y;
1266         } else if ( gr_screen.clip_bottom+y > gr_screen.max_h-1 )       {
1267                 gr_screen.clip_bottom = gr_screen.max_h-1-y;
1268         }
1269
1270         gr_screen.clip_width = gr_screen.clip_right - gr_screen.clip_left + 1;
1271         gr_screen.clip_height = gr_screen.clip_bottom - gr_screen.clip_top + 1;
1272 }
1273
1274 // resets the clipping region to entire screen
1275 //
1276 // should call this before gr_surface_flip() if you clipped some portion of the screen but still 
1277 // want a full-screen display
1278 void grx_reset_clip()
1279 {
1280         gr_screen.offset_x = 0;
1281         gr_screen.offset_y = 0;
1282         gr_screen.clip_left = 0;
1283         gr_screen.clip_top = 0;
1284         gr_screen.clip_right = gr_screen.max_w - 1;
1285         gr_screen.clip_bottom = gr_screen.max_h - 1;
1286         gr_screen.clip_width = gr_screen.max_w;
1287         gr_screen.clip_height = gr_screen.max_h;
1288 }
1289
1290
1291 // Sets the current bitmap
1292 void grx_set_bitmap( int bitmap_num, int alphablend_mode, int bitblt_mode, float alpha, int sx, int sy )
1293 {
1294         gr_screen.current_alpha = alpha;
1295         gr_screen.current_alphablend_mode = alphablend_mode;
1296         gr_screen.current_bitblt_mode = bitblt_mode;
1297         gr_screen.current_bitmap = bitmap_num;
1298         gr_screen.current_bitmap_sx = sx;
1299         gr_screen.current_bitmap_sy = sy;
1300 }
1301
1302
1303 // clears entire clipping region to black.
1304 void grx_clear()
1305 {
1306         gr_lock();
1307
1308         int i,w;
1309         ubyte *pDestBits;
1310
1311         w = gr_screen.clip_right-gr_screen.clip_left+1;
1312         for (i=gr_screen.clip_top; i<=gr_screen.clip_bottom; i++)       {
1313                 pDestBits = GR_SCREEN_PTR(ubyte,gr_screen.clip_left,i);
1314                 memset( pDestBits, 0, w );
1315         }       
1316
1317         gr_unlock();
1318 }
1319
1320
1321
1322 void grx_start_frame()
1323 {
1324 }
1325
1326 void grx_stop_frame()
1327 {
1328 }
1329
1330 void gr_soft_fog_set(int fog_mode, int r, int g, int b, float near, float far)
1331 {
1332 }
1333
1334 void gr_soft_get_pixel(int x, int y, int *r, int *g, int *b)
1335 {
1336 }
1337
1338 void grx_fade_in(int instantaneous);
1339 void grx_fade_out(int instantaneous);
1340 void grx_flash(int r, int g, int b);
1341
1342 static ubyte *Gr_saved_screen = NULL;
1343 static uint Gr_saved_screen_palette_checksum = 0;
1344 static ubyte Gr_saved_screen_palette[768];
1345
1346 int gr8_save_screen()
1347 {
1348         int i;
1349         gr_reset_clip();
1350
1351         if (gr_screen.bits_per_pixel != 8) {
1352                 mprintf(( "Save Screen only works in 8 bpp!\n" ));
1353                 return -1;
1354         }
1355
1356         if ( Gr_saved_screen )  {
1357                 mprintf(( "Screen alread saved!\n" ));
1358                 return -1;
1359         }
1360
1361         Gr_saved_screen = (ubyte *)malloc( gr_screen.max_w*gr_screen.max_h );
1362         if (!Gr_saved_screen) {
1363                 mprintf(( "Couldn't get memory for saved screen!\n" ));
1364                 return -1;
1365         }
1366
1367         Gr_saved_screen_palette_checksum = gr_palette_checksum;
1368         memcpy( Gr_saved_screen_palette, gr_palette, 768 );
1369
1370         gr_lock();
1371
1372         for (i=0; i<gr_screen.max_h; i++ )      {
1373                 ubyte *dptr = GR_SCREEN_PTR(ubyte,0,i);
1374                 memcpy( &Gr_saved_screen[gr_screen.max_w*i], dptr, gr_screen.max_w );
1375         }
1376
1377         gr_unlock();
1378
1379         return 0;
1380 }
1381
1382
1383 void gr8_restore_screen(int id)
1384 {
1385         int i;
1386         gr_reset_clip();
1387
1388         if ( !Gr_saved_screen ) {
1389                 gr_clear();
1390                 return;
1391         }
1392
1393         if ( Gr_saved_screen_palette_checksum != gr_palette_checksum )  {
1394                 // Palette changed! Remap the bitmap!
1395                 ubyte xlat[256];
1396                 for (i=0; i<256; i++ )  {
1397                         xlat[i] = (ubyte)palette_find( Gr_saved_screen_palette[i*3+0], Gr_saved_screen_palette[i*3+1], Gr_saved_screen_palette[i*3+2] );
1398                 }       
1399
1400                 for (i=0; i<gr_screen.max_h*gr_screen.max_w; i++ )      {
1401                         Gr_saved_screen[i] = xlat[Gr_saved_screen[i]];
1402                 }
1403
1404                 memcpy( Gr_saved_screen_palette, gr_palette, 768 );
1405                 Gr_saved_screen_palette_checksum = gr_palette_checksum;
1406         }
1407
1408         gr_lock();
1409
1410         for (i=0; i<gr_screen.max_h; i++ )      {
1411                 ubyte *dptr = GR_SCREEN_PTR(ubyte,0,i);
1412                 memcpy( dptr, &Gr_saved_screen[gr_screen.max_w*i], gr_screen.max_w );
1413         }
1414
1415         gr_unlock();
1416 }
1417
1418
1419 void gr8_free_screen(int id)
1420 {
1421         if ( Gr_saved_screen )  {
1422                 free( Gr_saved_screen );
1423                 Gr_saved_screen = NULL;
1424         }
1425 }
1426
1427 static int Gr8_dump_frames = 0;
1428 static ubyte *Gr8_dump_buffer = NULL;
1429 static int Gr8_dump_frame_number = 0;
1430 static int Gr8_dump_frame_count = 0;
1431 static int Gr8_dump_frame_count_max = 0;
1432 static int Gr8_dump_frame_size = 0;
1433
1434
1435 void gr8_dump_frame_start(int first_frame, int frames_between_dumps)
1436 {
1437         if ( Gr8_dump_frames )  {
1438                 Int3();         //  We're already dumping frames.  See John.
1439                 return;
1440         }       
1441         Gr8_dump_frames = 1;
1442         Gr8_dump_frame_number = first_frame;
1443         Gr8_dump_frame_count = 0;
1444         Gr8_dump_frame_count_max = frames_between_dumps;
1445         Gr8_dump_frame_size = 640 * 480;
1446         
1447         if ( !Gr8_dump_buffer ) {
1448                 int size = Gr8_dump_frame_count_max * Gr8_dump_frame_size;
1449                 Gr8_dump_buffer = (ubyte *)malloc(size);
1450                 if ( !Gr8_dump_buffer ) {
1451                         Error(LOCATION, "Unable to malloc %d bytes for dump buffer", size );
1452                 }
1453         }
1454 }
1455
1456 // A hacked function to dump the frame buffer contents
1457 void gr8_dump_screen_hack( void * dst )
1458 {
1459         int i;
1460
1461         gr_lock();
1462         for (i = 0; i < 480; i++)       {
1463                 memcpy( (ubyte *)dst+(i*640), GR_SCREEN_PTR(ubyte,0,i), 640 );
1464         }
1465         gr_unlock();
1466 }
1467
1468 void gr8_flush_frame_dump()
1469 {
1470         ubyte *buffer[480];
1471         char filename[MAX_PATH_LEN], *movie_path = "";
1472
1473         int i;
1474         for (i = 0; i < Gr8_dump_frame_count; i++) {
1475                 int j;
1476
1477                 for ( j = 0; j < 480; j++ )
1478                         buffer[j] = Gr8_dump_buffer+(i*Gr8_dump_frame_size)+(j*640);
1479
1480                 sprintf(filename, NOX("%sfrm%04d"), movie_path, Gr8_dump_frame_number );
1481                 pcx_write_bitmap(filename, 640, 480, buffer, gr_palette);
1482                 Gr8_dump_frame_number++;
1483         }
1484 }
1485
1486 void gr8_dump_frame()
1487 {
1488         // A hacked function to dump the frame buffer contents
1489         gr8_dump_screen_hack( Gr8_dump_buffer+(Gr8_dump_frame_count*Gr8_dump_frame_size) );
1490
1491         Gr8_dump_frame_count++;
1492
1493         if ( Gr8_dump_frame_count == Gr8_dump_frame_count_max ) {
1494                 gr8_flush_frame_dump();
1495                 Gr8_dump_frame_count = 0;
1496         }
1497 }
1498
1499 void grx_get_region(int front, int w, int h, ubyte *data)
1500 {
1501 }
1502
1503 // resolution checking
1504 int gr_soft_supports_res_ingame(int res)
1505 {
1506         return 1;
1507 }
1508
1509 int gr_soft_supports_res_interface(int res)
1510 {
1511         return 1;
1512 }
1513
1514 void gr8_dump_frame_stop()
1515 {
1516         if ( !Gr8_dump_frames ) {
1517                 Int3();         //  We're not dumping frames.  See John.
1518                 return;
1519         }       
1520
1521         // dump any remaining frames
1522         gr8_flush_frame_dump();
1523         
1524         Gr8_dump_frames = 0;
1525         if ( Gr8_dump_buffer )  {
1526                 free(Gr8_dump_buffer);
1527                 Gr8_dump_buffer = NULL;
1528         }
1529 }
1530
1531 void gr_soft_set_cull(int cull)
1532 {
1533 }
1534
1535 // cross fade
1536 void gr_soft_cross_fade(int bmap1, int bmap2, int x1, int y1, int x2, int y2, float pct)
1537 {
1538         if ( pct <= 50 )        {
1539                 gr_set_bitmap(bmap1);
1540                 gr_bitmap(x1, y1);
1541         } else {
1542                 gr_set_bitmap(bmap2);
1543                 gr_bitmap(x2, y2);
1544         }       
1545 }
1546
1547 // filter
1548 void gr_soft_filter_set(int filter)
1549 {
1550 }
1551
1552 // tcache
1553 int gr_soft_tcache_set(int bitmap_id, int bitmap_type, float *u_ratio, float *v_ratio, int fail_on_full = 0, int sx = -1, int sy = -1, int force = 0 )
1554 {
1555         return 1;
1556 }
1557
1558 // clear color
1559 void gr_soft_set_clear_color(int r, int g, int b)
1560 {
1561 }
1562
1563 extern uint Gr_signature;
1564
1565 //extern void gr_set_palette_internal(char *name, ubyte *pal);  
1566
1567 void gr8_set_gamma(float gamma)
1568 {
1569         Gr_gamma = gamma;
1570         Gr_gamma_int = int(Gr_gamma*100);
1571
1572         // Create the Gamma lookup table
1573         int i;
1574         for (i=0; i<256; i++ )  {
1575                 int v = fl2i(pow(i2fl(i)/255.0f, 1.0f/Gr_gamma)*255.0f);
1576                 if ( v > 255 ) {
1577                         v = 255;
1578                 } else if ( v < 0 )     {
1579                         v = 0;
1580                 }
1581                 Gr_gamma_lookup[i] = v;
1582         }
1583
1584 //      ubyte new_pal[768];
1585 //      if ( gr_screen.bits_per_pixel!=8 )      return;
1586 //
1587 //      for (i=0; i<768; i++ )  {
1588 //              new_pal[i] = ubyte(Gr_gamma_lookup[gr_palette[i]]);
1589 //      }
1590 //      grx_change_palette( new_pal );
1591
1592         gr_screen.signature = Gr_signature++;
1593 }
1594
1595
1596 void gr_soft_init()
1597 {
1598 //      int i;
1599         
1600         // software mode only supports 640x480
1601         Assert(gr_screen.res == GR_640);
1602         if(gr_screen.res != GR_640){
1603                 gr_screen.res = GR_640;
1604                 gr_screen.max_w = 640;
1605                 gr_screen.max_h = 480;
1606         }
1607
1608         // Prepare the window to go full screen
1609 #ifdef PLAT_UNIX
1610         if (SDL_InitSubSystem (SDL_INIT_VIDEO) < 0)
1611         {
1612                 fprintf (stderr, "Couldn't initialize SDL: %s", SDL_GetError());
1613                 exit (1);
1614         }
1615
1616         atexit (SDL_Quit);
1617         
1618         soft_surface = SDL_SetVideoMode (640,480,8,0);
1619         if (soft_surface == NULL) {
1620                 fprintf (stderr, "Couldn't set 640x480x8 mode: %s", SDL_GetError());
1621                 exit (1);
1622         }
1623 #else
1624         HWND hwnd = (HWND)os_get_window();
1625
1626         if ( hwnd )     {
1627                 DWORD style, exstyle;
1628                 RECT            client_rect;
1629
1630                 exstyle = 0;
1631                 style = WS_CAPTION | WS_SYSMENU;
1632                 
1633                 //      Create Game Window
1634                 client_rect.left = client_rect.top = 0;
1635                 client_rect.right = gr_screen.max_w;
1636                 client_rect.bottom = gr_screen.max_h;
1637                 AdjustWindowRect(&client_rect,style,FALSE);
1638
1639                 RECT work_rect;
1640                 SystemParametersInfo( SPI_GETWORKAREA, 0, &work_rect, 0 );
1641                 int x = work_rect.left + (( work_rect.right - work_rect.left )-(client_rect.right - client_rect.left))/2;
1642                 int y = work_rect.top;
1643                 if ( x < work_rect.left ) {
1644                         x = work_rect.left;
1645                 }
1646                 int WinX = x;
1647                 int WinY = y;
1648                 int WinW = client_rect.right - client_rect.left;
1649                 int WinH = client_rect.bottom - client_rect.top;
1650
1651                 ShowWindow(hwnd, SW_SHOWNORMAL );
1652                 SetWindowLong( hwnd, GWL_STYLE, style );
1653                 SetWindowLong( hwnd, GWL_EXSTYLE, exstyle );
1654                 SetWindowPos( hwnd, HWND_NOTOPMOST, WinX, WinY, WinW, WinH, SWP_SHOWWINDOW );
1655                 SetActiveWindow(hwnd);
1656                 SetForegroundWindow(hwnd);
1657         }
1658 #endif
1659
1660         Palette_flashed = 0;
1661         Palette_flashed_last_frame = 0;
1662
1663         gr_screen.bits_per_pixel = 8;
1664         gr_screen.bytes_per_pixel = 1;
1665
1666         gr_buffer_create( gr_screen.max_w, gr_screen.max_h, gr_screen.bits_per_pixel );
1667
1668 #ifdef PLAT_UNIX
1669         gr_screen.offscreen_buffer_base = gr_screen.offscreen_buffer = soft_surface->pixels;
1670         gr_screen.rowsize = soft_surface->pitch;
1671 #else
1672         gr_screen.offscreen_buffer_base = lpDibBits;
1673
1674         gr_screen.rowsize = DibInfo.Header.biWidth*((gr_screen.bits_per_pixel+7)/8);
1675         Assert( DibInfo.Header.biWidth == gr_screen.max_w );
1676
1677         if (DibInfo.Header.biHeight > 0)        {
1678                 // top down 
1679                 gr_screen.offscreen_buffer = (void *)((uint)gr_screen.offscreen_buffer_base + (gr_screen.max_h - 1) * gr_screen.rowsize);
1680                 gr_screen.rowsize *= -1;
1681         } else {
1682                 // top up
1683                 gr_screen.offscreen_buffer = gr_screen.offscreen_buffer_base;
1684         }
1685 #endif
1686
1687         grx_init_alphacolors();
1688
1689         gr_screen.gf_flip = grx_flip;
1690         gr_screen.gf_flip_window = grx_flip_window;
1691         gr_screen.gf_set_clip = grx_set_clip;
1692         gr_screen.gf_reset_clip = grx_reset_clip;
1693         gr_screen.gf_set_font = grx_set_font;
1694         gr_screen.gf_set_color = grx_set_color;
1695         gr_screen.gf_set_bitmap = grx_set_bitmap;
1696         gr_screen.gf_create_shader = grx_create_shader;
1697         gr_screen.gf_set_shader = grx_set_shader;
1698         gr_screen.gf_clear = grx_clear;
1699         // gr_screen.gf_bitmap = grx_bitmap;
1700         // ]gr_screen.gf_bitmap_ex = grx_bitmap_ex;
1701
1702         gr_screen.gf_aabitmap = grx_aabitmap;
1703         gr_screen.gf_aabitmap_ex = grx_aabitmap_ex;
1704
1705         gr_screen.gf_rect = grx_rect;
1706         gr_screen.gf_shade = gr8_shade;
1707         gr_screen.gf_string = gr8_string;
1708         gr_screen.gf_circle = gr8_circle;
1709
1710         gr_screen.gf_line = gr8_line;
1711         gr_screen.gf_aaline = gr8_aaline;
1712         gr_screen.gf_pixel = gr8_pixel;
1713         gr_screen.gf_scaler = gr8_scaler;
1714         gr_screen.gf_aascaler = gr8_aascaler;
1715         gr_screen.gf_tmapper = grx_tmapper;
1716
1717         gr_screen.gf_gradient = gr8_gradient;
1718
1719         gr_screen.gf_set_palette = grx_set_palette;
1720         gr_screen.gf_get_color = grx_get_color;
1721         gr_screen.gf_init_color = grx_init_color;
1722         gr_screen.gf_init_alphacolor = grx_init_alphacolor;
1723         gr_screen.gf_set_color_fast = grx_set_color_fast;
1724         gr_screen.gf_print_screen = grx_print_screen;
1725         gr_screen.gf_start_frame = grx_start_frame;
1726         gr_screen.gf_stop_frame = grx_stop_frame;
1727
1728         gr_screen.gf_fade_in = grx_fade_in;
1729         gr_screen.gf_fade_out = grx_fade_out;
1730         gr_screen.gf_flash = grx_flash;
1731
1732
1733         // Retrieves the zbuffer mode.
1734         gr_screen.gf_zbuffer_get = gr8_zbuffer_get;
1735         gr_screen.gf_zbuffer_set = gr8_zbuffer_set;
1736         gr_screen.gf_zbuffer_clear = gr8_zbuffer_clear;
1737
1738         gr_screen.gf_save_screen = gr8_save_screen;
1739         gr_screen.gf_restore_screen = gr8_restore_screen;
1740         gr_screen.gf_free_screen = gr8_free_screen;
1741
1742         // Screen dumping stuff
1743         gr_screen.gf_dump_frame_start = gr8_dump_frame_start;
1744         gr_screen.gf_dump_frame_stop = gr8_dump_frame_stop;
1745         gr_screen.gf_dump_frame = gr8_dump_frame;
1746
1747         // Gamma stuff
1748         gr_screen.gf_set_gamma = gr8_set_gamma;
1749
1750         // Lock/unlock stuff
1751         gr_screen.gf_lock = gr_soft_lock;
1752         gr_screen.gf_unlock = gr_soft_unlock;
1753
1754         // region
1755         gr_screen.gf_get_region = grx_get_region;
1756
1757         // fog stuff
1758         gr_screen.gf_fog_set = gr_soft_fog_set; 
1759
1760         // pixel get
1761         gr_screen.gf_get_pixel = gr_soft_get_pixel;
1762
1763         // poly culling
1764         gr_screen.gf_set_cull = gr_soft_set_cull;
1765
1766         // cross fade
1767         gr_screen.gf_cross_fade = gr_soft_cross_fade;
1768
1769         // filter
1770         gr_screen.gf_filter_set = gr_soft_filter_set;
1771
1772         // tcache set
1773         gr_screen.gf_tcache_set = gr_soft_tcache_set;
1774
1775         // set clear color
1776         gr_screen.gf_set_clear_color = gr_soft_set_clear_color;
1777
1778         gr_reset_clip();
1779         gr_clear();
1780         gr_flip();
1781 }
1782
1783 void gr_soft_force_windowed()
1784 {
1785 }
1786
1787 void gr_soft_cleanup()
1788 {
1789         if (Gr_soft_inited) {
1790                 gr_buffer_release();
1791                 Gr_soft_inited = 0;
1792         }
1793 }
1794
1795 void grx_change_palette( ubyte * new_pal )
1796 {
1797 #ifdef PLAT_UNIX
1798         int i;
1799         SDL_Color colors[256];
1800         for (i=0; i<256; i++)
1801         {
1802                 colors[i].r = new_pal[i*3+0];
1803                 colors[i].g = new_pal[i*3+1];
1804                 colors[i].b = new_pal[i*3+2];
1805         }
1806         SDL_SetColors (soft_surface, colors, 0, 256);
1807 #else
1808         if ( hPalette ) {
1809                 if (hDibDC)
1810                         SelectPalette( hDibDC, hOldPalette, FALSE );
1811                 if (!DeleteObject(hPalette))
1812                         Int3();
1813                 hPalette = NULL;
1814         }
1815
1816         hPalette = gr_create_palette_256(new_pal);      // All 256 mapped one-to-one, but BLT's are slow.
1817
1818         if ( hDibDC )   {
1819                 int i; 
1820                 for (i=0; i<256; i++ )  {
1821                         DibInfo.Colors.aColors[i].rgbRed = new_pal[i*3+0];
1822                         DibInfo.Colors.aColors[i].rgbGreen = new_pal[i*3+1];
1823                         DibInfo.Colors.aColors[i].rgbBlue = new_pal[i*3+2];
1824                         DibInfo.Colors.aColors[i].rgbReserved = 0;
1825                 }
1826
1827                 hOldPalette = SelectPalette( hDibDC, hPalette, FALSE );
1828                 SetDIBColorTable( hDibDC, 0, 256, DibInfo.Colors.aColors );
1829         }
1830 #endif
1831 }
1832
1833 void grx_flash( int r, int g, int b )
1834 {
1835         int t,i;
1836         ubyte new_pal[768];
1837
1838         if ( (r==0) && (g==0) && (b==0) )       {
1839                 return;
1840         }
1841
1842         Palette_flashed++;
1843
1844         for (i=0; i<256; i++ )  {
1845                 t = gr_palette[i*3+0] + r;
1846                 if ( t < 0 ) t = 0; else if (t>255) t = 255;
1847                 new_pal[i*3+0] = (ubyte)t;
1848
1849                 t = gr_palette[i*3+1] + g;
1850                 if ( t < 0 ) t = 0; else if (t>255) t = 255;
1851                 new_pal[i*3+1] = (ubyte)t;
1852
1853                 t = gr_palette[i*3+2] + b;
1854                 if ( t < 0 ) t = 0; else if (t>255) t = 255;
1855                 new_pal[i*3+2] = (ubyte)t;
1856         }
1857
1858         grx_change_palette( new_pal );
1859 }
1860
1861 #ifndef HARDWARE_ONLY
1862 static int gr_palette_faded_out = 0;
1863 #endif
1864
1865 #define FADE_TIME (F1_0/4)              // How long to fade out
1866
1867 void grx_fade_out(int instantaneous)    
1868 {
1869 #ifndef HARDWARE_ONLY
1870         int i;
1871         ubyte new_pal[768];
1872
1873         if (!gr_palette_faded_out) {
1874
1875                 if ( !instantaneous )   {
1876         
1877                         int count = 0;
1878                         fix start_time, stop_time, t1;
1879
1880                         start_time = timer_get_fixed_seconds();
1881                         t1 = 0;
1882
1883                         do {
1884                                 for (i=0; i<768; i++ )  {               
1885                                         int c = (gr_palette[i]*(FADE_TIME-t1))/FADE_TIME;
1886                                         if (c < 0 )
1887                                                 c = 0;
1888                                         else if ( c > 255 )
1889                                                 c = 255;
1890                         
1891                                         new_pal[i] = (ubyte)c;
1892                                 }
1893                                 grx_change_palette( new_pal );
1894                                 gr_flip();
1895                                 count++;
1896
1897                                 t1 = timer_get_fixed_seconds() - start_time;
1898
1899                         } while ( (t1 < FADE_TIME) && (t1>=0) );                // Loop as long as time not up and timer hasn't rolled
1900
1901                         stop_time = timer_get_fixed_seconds();
1902
1903                         mprintf(( "Took %d frames (and %.1f secs) to fade out\n", count, f2fl(stop_time-start_time) ));
1904                 
1905                 }
1906                 gr_palette_faded_out = 1;
1907         }
1908
1909         gr_reset_clip();
1910         gr_clear();
1911         gr_flip();
1912         memset( new_pal, 0, 768 );
1913         grx_change_palette( new_pal );
1914 #else
1915         Int3();
1916 #endif
1917 }
1918
1919
1920 void grx_fade_in(int instantaneous)     
1921 {
1922 #ifndef HARDWARE_ONLY
1923         int i;
1924         ubyte new_pal[768];
1925
1926         if (gr_palette_faded_out)       {
1927
1928                 if ( !instantaneous )   {
1929                         int count = 0;
1930                         fix start_time, stop_time, t1;
1931
1932                         start_time = timer_get_fixed_seconds();
1933                         t1 = 0;
1934
1935                         do {
1936                                 for (i=0; i<768; i++ )  {               
1937                                         int c = (gr_palette[i]*t1)/FADE_TIME;
1938                                         if (c < 0 )
1939                                                 c = 0;
1940                                         else if ( c > 255 )
1941                                                 c = 255;
1942                         
1943                                         new_pal[i] = (ubyte)c;
1944                                 }
1945                                 grx_change_palette( new_pal );
1946                                 gr_flip();
1947                                 count++;
1948
1949                                 t1 = timer_get_fixed_seconds() - start_time;
1950
1951                         } while ( (t1 < FADE_TIME) && (t1>=0) );                // Loop as long as time not up and timer hasn't rolled
1952
1953                         stop_time = timer_get_fixed_seconds();
1954
1955                         mprintf(( "Took %d frames (and %.1f secs) to fade in\n", count, f2fl(stop_time-start_time) ));
1956                 }
1957                 gr_palette_faded_out = 0;
1958         }
1959
1960         memcpy( new_pal, gr_palette, 768 );
1961         grx_change_palette( new_pal );
1962 #else 
1963         Int3();
1964 #endif
1965 }
1966
1967