optimizations and best color depth attempt
[fbgfx] / src / tunnel.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <assert.h>
5 #include <imago2.h>
6 #include "tpool.h"
7 #include "tunnel.h"
8
9 #define TEX_FNAME       "data/grid.png"
10 #define TEX_USCALE      4
11 #define TEX_VSCALE      2
12
13 #define USCALE  2
14 #define VSCALE  1
15
16 extern unsigned long time_msec;
17
18 static void (*draw_tunnel_range)(void*, int, int);
19
20 static void draw_tunnel_range16(void *pixels, int starty, int num_lines);
21 static void draw_tunnel_range32(void *pixels, int starty, int num_lines);
22 static int count_bits(unsigned int x);
23 static int count_zeros(unsigned int x);
24
25 static int xsz, ysz, vxsz, vysz;
26 static unsigned int *tunnel_map;
27 static unsigned char *tunnel_fog;
28
29 static int tex_xsz, tex_ysz;
30 static unsigned int *tex_pixels;
31 static int tex_xshift, tex_yshift;
32 static unsigned int tex_xmask, tex_ymask;
33
34 static struct thread_pool *tpool;
35
36
37 int init_tunnel(int x, int y, int bpp)
38 {
39         int i, j, n;
40         unsigned int *tmap;
41         unsigned char *fog;
42         float aspect = (float)x / (float)y;
43
44         switch(bpp) {
45         case 16:
46                 draw_tunnel_range = draw_tunnel_range16;
47                 break;
48         case 32:
49                 draw_tunnel_range = draw_tunnel_range32;
50                 break;
51         default:
52                 fprintf(stderr, "unsupported color depth: %d\n", bpp);
53                 return -1;
54         }
55
56         xsz = x;
57         ysz = y;
58         vxsz = xsz / USCALE;
59         vysz = ysz / VSCALE;
60
61         if(!(tunnel_map = malloc(vxsz * vysz * sizeof *tunnel_map))) {
62                 fprintf(stderr, "failed to allocate tunnel map\n");
63                 return -1;
64         }
65         if(!(tunnel_fog = malloc(vxsz * vysz))) {
66                 fprintf(stderr, "failed to allocate tunnel fog map\n");
67                 return -1;
68         }
69
70         tmap = tunnel_map;
71         fog = tunnel_fog;
72
73         for(i=0; i<vysz; i++) {
74                 float y = 2.0 * (float)i / (float)vysz - 1.0;
75                 for(j=0; j<vxsz; j++) {
76                         float x = aspect * (2.0 * (float)j / (float)vxsz - 1.0);
77                         float tu = atan2(y, x) / M_PI * 0.5 + 0.5;
78                         float d = sqrt(x * x + y * y);
79                         float tv = d == 0.0 ? 0.0 : 1.0 / d;
80
81                         int tx = (int)(tu * 65535.0 * TEX_USCALE) & 0xffff;
82                         int ty = (int)(tv * 65535.0 * TEX_VSCALE) & 0xffff;
83
84                         int f = (int)(d * 95.0);
85
86                         *tmap++ = (tx << 16) | ty;
87                         *fog++ = f > 255 ? 255 : f;
88                 }
89         }
90
91         if(!(tex_pixels = img_load_pixels(TEX_FNAME, &tex_xsz, &tex_ysz, IMG_FMT_RGBA32))) {
92                 fprintf(stderr, "failed to load image " TEX_FNAME "\n");
93                 return -1;
94         }
95         if((count_bits(tex_xsz) | count_bits(tex_ysz)) != 1) {
96                 fprintf(stderr, "non-pow2 image (%dx%d)\n", tex_xsz, tex_ysz);
97                 return -1;
98         }
99
100         n = count_zeros(tex_xsz);
101         for(i=0; i<n; i++) {
102                 tex_xmask |= 1 << i;
103         }
104         tex_xshift = n;
105
106         n = count_zeros(tex_ysz);
107         for(i=0; i<n; i++) {
108                 tex_ymask |= 1 << i;
109         }
110         tex_yshift = n;
111
112         if(!(tpool = tpool_create(0))) {
113                 fprintf(stderr, "failed to create thread pool\n");
114                 return -1;
115         }
116
117         return 0;
118 }
119
120 void destroy_tunnel(void)
121 {
122         tpool_destroy(tpool);
123         free(tunnel_map);
124         free(tunnel_fog);
125 }
126
127 #define NUM_WORK_ITEMS  32
128
129 static struct work {
130         void *pixels;
131         int starty, num_lines;
132 } work[NUM_WORK_ITEMS];
133
134 static void work_func(void *cls)
135 {
136         struct work *w = (struct work*)cls;
137         draw_tunnel_range(w->pixels, w->starty, w->num_lines);
138 }
139
140 void draw_tunnel(void *pixels)
141 {
142         int i, num_lines = vysz / NUM_WORK_ITEMS;
143         for(i=0; i<NUM_WORK_ITEMS; i++) {
144                 work[i].pixels = pixels;
145                 work[i].starty = i * num_lines;
146                 work[i].num_lines = num_lines;
147
148                 tpool_enqueue(tpool, work + i, work_func, 0);
149         }
150         tpool_wait(tpool);
151 }
152
153 static void tunnel_color(int *rp, int *gp, int *bp, long toffs, unsigned int tpacked, int fog)
154 {
155         int r, g, b;
156         unsigned int col;
157         unsigned int tx = (((tpacked >> 16) & 0xffff) << tex_xshift) >> 16;
158         unsigned int ty = ((tpacked & 0xffff) << tex_yshift) >> 16;
159         tx += toffs;
160         ty += toffs << 1;
161
162         tx &= tex_xmask;
163         ty &= tex_ymask;
164
165         col = tex_pixels[(ty << tex_xshift) + tx];
166         r = col & 0xff;
167         g = (col >> 8) & 0xff;
168         b = (col >> 16) & 0xff;
169
170         *rp = (r * fog) >> 8;
171         *gp = (g * fog) >> 8;
172         *bp = (b * fog) >> 8;
173 }
174
175 #define PACK_RGB16(r, g, b) \
176         (((((r) >> 3) & 0x1f) << 11) | ((((g) >> 2) & 0x3f) << 5) | (((b) >> 3) & 0x1f))
177 #define PACK_RGB32(r, g, b) \
178         ((((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff))
179
180 static void draw_tunnel_range16(void *pix, int starty, int num_lines)
181 {
182         int i, j;
183         unsigned int *tmap = tunnel_map + starty * vxsz;
184         unsigned char *fog = tunnel_fog + starty * vxsz;
185
186         long toffs = time_msec / 4;
187         unsigned int *pixels = (unsigned int*)pix + starty * (xsz >> 1);
188
189         for(i=0; i<num_lines; i++) {
190                 for(j=0; j<vxsz; j++) {
191                         unsigned int col;
192                         int r, g, b;
193
194                         tunnel_color(&r, &g, &b, toffs, *tmap++, *fog++);
195                         col = PACK_RGB16(r, g, b);
196                         *pixels++ = col;
197                 }
198         }
199 }
200
201 static void draw_tunnel_range32(void *pix, int starty, int num_lines)
202 {
203         int i, j;
204         unsigned int *tmap = tunnel_map + starty * vxsz;
205         unsigned char *fog = tunnel_fog + starty * vxsz;
206
207         long toffs = time_msec / 4;
208         unsigned int *pixels = (unsigned int*)pix + starty * xsz * VSCALE;
209
210         for(i=0; i<num_lines; i++) {
211                 for(j=0; j<vxsz; j++) {
212                         unsigned int col;
213                         int r, g, b;
214
215                         tunnel_color(&r, &g, &b, toffs, *tmap++, *fog++);
216                         col = PACK_RGB32(r, g, b);
217
218                         *pixels++ = col;
219                         *pixels++ = col;
220                 }
221         }
222 }
223
224 static int count_bits(unsigned int x)
225 {
226         int i, nbits = 0;
227         for(i=0; i<32; i++) {
228                 if(x & 1) ++nbits;
229                 x >>= 1;
230         }
231         return nbits;
232 }
233
234 static int count_zeros(unsigned int x)
235 {
236         int i, num = 0;
237         for(i=0; i<32; i++) {
238                 if(x & 1) break;
239                 ++num;
240                 x >>= 1;
241         }
242         return num;
243 }