1 /*
2 * Kuklomenos
3 * Copyright (C) 2008-2009 Martin Bays <mbays@sdf.lonestar.org>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/.
17 */
18
19 #include "background.h"
20 #include "random.h"
21 #include "coords.h"
22 #include "settings.h"
23
24 #include <vector>
25 using namespace std;
26
27 #include <SDL/SDL.h>
28 #include <SDL_gfxPrimitivesDirty.h>
29
30 SDL_Surface* background = NULL;
31
setBackground(SDL_Surface * screen)32 void setBackground(SDL_Surface* screen)
33 {
34 if (background)
35 SDL_FreeSurface(background);
36 if (settings.bgType == BG_NONE)
37 background = NULL;
38 else
39 background = SDL_CreateRGBSurface(SDL_SWSURFACE, screen->w, screen->h,
40 screen->format->BitsPerPixel, 0,0,0,0);
41 drawBackground(screen);
42 }
43
44 Uint32 randomStarColour(bool interesting=false);
45
drawBackground(SDL_Surface * screen)46 void drawBackground(SDL_Surface* screen)
47 {
48 if (!background)
49 {
50 // background==NULL: use black background
51 SDL_FillRect(screen, NULL, 0);
52 return;
53 }
54
55 SDL_FillRect(background, NULL, 0);
56
57 if (settings.bgType == BG_SOLAR)
58 {
59 // Position a star offscreen (at least a screen's diagonal away from
60 // any onscreen point)
61 const double theta = ranf(2*PI);
62 const double dist = (ranf(2)+3)*sqrt(background->w*background->w +
63 background->h*background->h)/2;
64 const double cx = background->w/2 + dist*cos(theta);
65 const double cy = background->h/2 + dist*sin(theta);
66
67 const Uint32 colour = randomStarColour(true);
68 const int starColour[3] = {
69 (colour & 0xff000000) >> 24,
70 (colour & 0x00ff0000) >> 16,
71 (colour & 0x0000ff00) >> 8 };
72
73 const Uint8 brightness = 0x70 + rani(0x20);
74 const double closestSqDist = background->w*background->w +
75 background->h*background->h;
76
77 // we want gaussian dither, but don't want to calculate a fresh
78 // gaussian for each pixel. So we sample, then use random samples for
79 // the dither.
80 const int bpp = background->format->BitsPerPixel;
81 static const int gaussianSampleSize = 200;
82 int gaussianSample[gaussianSampleSize];
83 for (int i=0; i < gaussianSampleSize; i++)
84 gaussianSample[i] = int(gaussian() * (
85 bpp == 32 ? 0x02 :
86 bpp == 24 ? 0x03 :
87 0x04));
88
89 for (int x=0; x < background->w; x++)
90 for (int y=0; y < background->h; y++)
91 {
92 // inverse square law for the intensity, with a random
93 // dithering effect to reduce ugly banding
94 const double sqDist = ((x-cx)*(x-cx) + (y-cy)*(y-cy));
95 const float intensity = closestSqDist / sqDist;
96
97 int c[3] = {
98 int(starColour[0] * intensity) +
99 gaussianSample[rani(gaussianSampleSize)],
100 int(starColour[1] * intensity) +
101 gaussianSample[rani(gaussianSampleSize)],
102 int(starColour[2] * intensity) +
103 gaussianSample[rani(gaussianSampleSize)] };
104
105 for (int i=0; i<3; i++)
106 {
107 if (c[i] > 0xff)
108 c[i] = 0xff;
109 if (c[i] < 0)
110 c[i] = 0;
111 }
112 pixelRGBA(background, x, y, c[0], c[1], c[2], brightness);
113 }
114 }
115
116 if (settings.bgType == BG_STARS || settings.bgType == BG_SOLAR)
117 for (int i=0;
118 i < (background->w * background->h / (400 + rani(800)));
119 i++)
120 {
121 pixelColor(background, rani(background->w), rani(background->h),
122 randomStarColour() + 0x30 + rani(0x90));
123 }
124
125 SDL_BlitSurface(background, NULL, screen, NULL);
126 }
127
addColour(Uint32 base,int dr=0,int dg=0,int db=0,int da=0)128 Uint32 addColour(Uint32 base, int dr=0, int dg=0, int db=0, int da=0)
129 {
130 Uint8 c[4] = { base >> 24, base >> 16 & 0xff, base >> 8 & 0xff, base & 0xff };
131 int dc[4] = { dr, dg, db, da };
132 for (int i = 0; i < 4; i++)
133 c[i] = (
134 0xff - c[i] < dc[i] ? 0xff :
135 c[i] < -dc[i] ? 0 :
136 c[i] + dc[i]);
137 return (c[0] << 24) + (c[1] << 16) + (c[2] << 8) + c[3];
138 }
139
randomStarColour(bool interesting)140 Uint32 randomStarColour(bool interesting)
141 {
142 // classColours based on data due to Mitchell Charity
143 // (http://www.vendian.org/mncharity/dir3/starcolor/)
144 // (multiplied through by 15/16 to leave room for dithering)
145 static const Uint32 classColours[] = { 0x91a5ef00, 0x9fb3ef00, 0xbdc9ef00,
146 0xe7e7ef00, 0xefe4db00, 0xefc49600, 0xefbf6800 };
147
148 int starClass;
149 const float r = ranf();
150 if (!interesting)
151 // roughly accurate frequency data based on that given in
152 // http://en.wikipedia.org/w/index.php?title=Stellar_classification&oldid=316377760
153 // (not followed exactly)
154 starClass =
155 r < 0.001 ? 0 : // Class O
156 r < 0.003 ? 1 : // Class B
157 r < 0.010 ? 2 : // Class A
158 r < 0.040 ? 3 : // Class F
159 r < 0.1 ? 4 : // Class G
160 r < 0.25 ? 5 : // Class K
161 6; // Class M
162 else
163 // let's magnify the chances of unlikely classes
164 starClass =
165 r < 0.02 ? 0 : // Class O
166 r < 0.05 ? 1 : // Class B
167 r < 0.1 ? 2 : // Class A
168 r < 0.2 ? 3 : // Class F
169 r < 0.3 ? 4 : // Class G
170 r < 0.5 ? 5 : // Class K
171 6; // Class M
172
173 const Uint32 baseColour = classColours[starClass];
174
175 // Randomly tweak the colours a little
176 return addColour(baseColour,
177 int(gaussian()*0x0b), int(gaussian()*0x0b), int(gaussian()*0x0b));
178 }
179
180