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