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