1 /**
2  * Graphics utilities: palettes...
3 
4  * Copyright (C) 2003  Shawn Betts
5  * Copyright (C) 2003, 2004, 2007, 2008, 2009  Sylvain Beucler
6 
7  * This file is part of GNU FreeDink
8 
9  * GNU FreeDink is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 3 of the
12  * License, or (at your option) any later version.
13 
14  * GNU FreeDink is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * General Public License for more details.
18 
19  * You should have received a copy of the GNU General Public License
20  * along with this program.  If not, see
21  * <http://www.gnu.org/licenses/>.
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include <config.h>
26 #endif
27 
28 #include "gfx_palette.h"
29 
30 #include <stdlib.h>
31 #include "SDL.h"
32 #include "SDL_image.h"
33 #include "gfx.h"
34 #include "io_util.h"
35 #include "paths.h"
36 #include "log.h"
37 
38 /* Copy of the physical screen palette - SDL provides a getter for the
39    logical palette, but not for the hardware palette */
40 static SDL_Color phys_palette[256];
41 
42 /* Palette change: with SDL, SDL_SetColors (aka
43    SDL_SetPalette(SDL_PHYSPAL)) apparently triggers a Flip, which
44    displays weird colors on the screen for a brief but displeasing
45    moment. Besides, SDL_Flip() does not refresh the hardware palette,
46    so update the physical palette needs to be done manually - but only
47    when the surface is already in its final form. The palette may need
48    to be changed before the screen content is ready, so we'll make the
49    engine know when he needs to refresh the physical palette: */
50 static int trigger_palette_change = 0;
51 
52 
53 /**
54  * Get local copy of physical palette
55  */
gfx_palette_get_phys(SDL_Color * palette)56 void gfx_palette_get_phys(SDL_Color* palette)
57 {
58   memcpy(palette, phys_palette, sizeof(phys_palette));
59 }
60 
61 /**
62  * Set physical palette
63  */
gfx_palette_set_phys(SDL_Color * new_palette)64 void gfx_palette_set_phys(SDL_Color* new_palette)
65 {
66   /* Now this one is tricky: DX/Woe has a "feature" where palette
67      indexes 0 and 255 are fixed to black and white,
68      respectively. This is the opposite of the default Dink palette
69      - which is why fill_screen(0) is black and not white as in the
70      Dink palette. This also makes "Lyna's Story"'s palette change a
71      bit ugly, because pure black and white colors are not reversed
72      when you enter "negative" color mode. This does not affect
73      other indexes. Technically this happens when you get a palette
74      from GetEntries(), and when you CreatePalette() without
75      specifying DDPCAPS_ALLOW256 (so respectively, in
76      change_screen_palette() and load_palette_from_*()). But well,
77      reproducing the bug is important for backward compatibility. */
78 
79   memcpy(phys_palette, new_palette, sizeof(phys_palette));
80 
81   phys_palette[0].r = 0;
82   phys_palette[0].g = 0;
83   phys_palette[0].b = 0;
84   phys_palette[255].r = 255;
85   phys_palette[255].g = 255;
86   phys_palette[255].b = 255;
87 
88   /* Applying the logical palette to the physical screen may trigger
89      a Flip, so don't do it right now */
90   trigger_palette_change = 1;
91 }
92 
93 /**
94  * Apply the recently-updated copy of the physical palette to the
95  * physical screen. This may trigger a Flip (so don't do that until
96  * Back is ready).
97  */
gfx_palette_apply_phys()98 void gfx_palette_apply_phys()
99 {
100   if (trigger_palette_change == 1)
101     gfx_palette_restore_phys();
102   trigger_palette_change = 0;
103 }
104 
105 /**
106  * Set the physical screen palette unconditionaly (e.g.: because it
107  * was changed, or lost)
108  */
gfx_palette_restore_phys()109 void gfx_palette_restore_phys()
110 {
111   SDL_SetPalette(GFX_lpDDSBack, SDL_PHYSPAL, phys_palette, 0, 256);
112 }
113 
114 
115 /* Generate a default/fallback 216 colors palette */
gfx_palette_reset()116 void gfx_palette_reset()
117 {
118   SDL_Color palette[256];
119   int i = 0;
120   int r, g, b;
121 
122   /* Set a 3,3,2 color cube */
123   for (r = 0; r < 256; r += 0x33)
124     {
125       for (g = 0; g < 256; g += 0x33)
126 	{
127 	  for (b = 0; b < 256; b += 0x33)
128 	    {
129 	      palette[i].r = r;
130 	      palette[i].g = g;
131 	      palette[i].b = b;
132 	      i++;
133 	    }
134 	}
135     }
136 
137   /* Set the rest of the colors to black */
138   for (; i < 256; i++)
139     palette[i].r
140       = palette[i].g
141       = palette[i].b
142       = 0;
143 
144   gfx_palette_set_phys(palette);
145 }
146 
147 /* Get a colors palette from the specified image */
148 int
gfx_palette_set_from_surface(SDL_Surface * bmp)149 gfx_palette_set_from_surface(SDL_Surface *bmp)
150 {
151   SDL_Color palette[256];
152   int i;
153 
154   if (bmp == NULL || bmp->format->palette == NULL)
155     return -1;
156 
157   for (i = 0; i < bmp->format->palette->ncolors; i++)
158     {
159       palette[i].r = bmp->format->palette->colors[i].r;
160       palette[i].g = bmp->format->palette->colors[i].g;
161       palette[i].b = bmp->format->palette->colors[i].b;
162     }
163 
164   gfx_palette_set_phys(palette);
165   return 0;
166 }
167 
168 /* Get a colors palette from the specified image */
169 int
gfx_palette_set_from_bmp(char * filename)170 gfx_palette_set_from_bmp(char *filename)
171 {
172   SDL_Surface *bmp;
173   int success = -1;
174   char *fullpath = NULL;
175 
176   fullpath = paths_dmodfile(filename);
177   bmp = IMG_Load(fullpath);
178   free(fullpath);
179   if (bmp == NULL)
180     {
181       fullpath = paths_fallbackfile(filename);
182       bmp = IMG_Load(fullpath);
183       free(fullpath);
184       if (bmp == NULL)
185 	{
186 	  log_error("load_palette_from_bmp: couldn't open '%s': %s",
187 		    filename, SDL_GetError());
188 	  return -1;
189 	}
190     }
191 
192   success = gfx_palette_set_from_surface(bmp);
193   SDL_FreeSurface(bmp);
194   return success;
195 }
196