8b16ebfcc62f886bcf2b916916be23c19335ead3
[mw/milkymist.git] / software / demo / osd.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
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, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <hal/vga.h>
19 #include <hal/tmu.h>
20 #include <hw/sysctl.h>
21 #include <hw/gpio.h>
22 #include <math.h>
23 #include <system.h>
24 #include <string.h>
25 #include <fatfs.h>
26 #include <blockdev.h>
27
28 #include "font.h"
29 #include "logo.h"
30 #include "renderer.h"
31 #include "version.h"
32 #include "osd.h"
33
34 int osd_x;
35 int osd_y;
36 #define OSD_W 600
37 #define OSD_H 160
38 #define OSD_CORNER 15
39 #define OSD_CHROMAKEY 0x001f
40
41 static struct tmu_vertex osd_vertices[TMU_MESH_MAXSIZE][TMU_MESH_MAXSIZE] __attribute__((aligned(8)));
42
43 static unsigned short int osd_fb[OSD_W*OSD_H] __attribute__((aligned(32)));
44
45 static struct font_context osd_font;
46
47 static void round_corners()
48 {
49         int i;
50         int d;
51         int x;
52
53         for(i=0;i<OSD_CORNER;i++) {
54                 d = OSD_CORNER - sqrtf(2*OSD_CORNER*i - i*i);
55                 for(x=0;x<d;x++) {
56                         osd_fb[i*OSD_W+x] = OSD_CHROMAKEY;
57                         osd_fb[i*OSD_W+(OSD_W-1-x)] = OSD_CHROMAKEY;
58                         osd_fb[(OSD_H-1-i)*OSD_W+x] = OSD_CHROMAKEY;
59                         osd_fb[(OSD_H-1-i)*OSD_W+(OSD_W-1-x)] = OSD_CHROMAKEY;
60                 }
61         }
62 }
63
64 #define LOGO_W 46
65 #define LOGO_H 46
66
67 static void logo()
68 {
69         int x, y;
70
71         for(y=0;y<LOGO_H;y++)
72                 for(x=0;x<LOGO_W;x++)
73                         osd_fb[(x+OSD_W-LOGO_W-OSD_CORNER)+OSD_W*(y+OSD_CORNER)] = ((unsigned short *)logo_raw)[x+LOGO_W*y];
74         font_draw_string(&osd_font, OSD_W-LOGO_W-OSD_CORNER, OSD_H-font_get_height(&osd_font), 0, VERSION);
75 }
76
77 static void init_ui();
78
79 void osd_init()
80 {
81         osd_x = (vga_hres - OSD_W) >> 1;
82         osd_y = vga_vres - OSD_H - 20;
83         
84         osd_vertices[0][0].x = 0;
85         osd_vertices[0][0].y = 0;
86         osd_vertices[0][1].x = OSD_W << TMU_FIXEDPOINT_SHIFT;
87         osd_vertices[0][1].y = 0;
88         osd_vertices[1][0].x = 0;
89         osd_vertices[1][0].y = OSD_H << TMU_FIXEDPOINT_SHIFT;
90         osd_vertices[1][1].x = OSD_W << TMU_FIXEDPOINT_SHIFT;
91         osd_vertices[1][1].y = OSD_H << TMU_FIXEDPOINT_SHIFT;
92
93         memset(osd_fb, 0, sizeof(osd_fb));
94         font_init_context(&osd_font, vera20_tff, osd_fb, OSD_W, OSD_H);
95         round_corners();
96         logo();
97
98         init_ui();
99 }
100
101 static int previous_keys;
102 static int osd_alpha;
103 static int osd_timer;
104
105 #define OSD_DURATION 90
106 #define OSD_MAX_ALPHA 40
107
108 #define OSD_MAX_USER_X (OSD_W-OSD_CORNER-LOGO_W)
109
110 #define MAX_PATCH_TITLE 45
111 #define MAX_PATCHES 128
112 static char current_patch[MAX_PATCH_TITLE+1];
113 static char patchlist_filenames[MAX_PATCHES][13];
114 static char patchlist_titles[MAX_PATCHES][MAX_PATCH_TITLE+1];
115 static int patchlist_n;
116 static int patchlist_sel;
117 static int patchlist_page;
118
119 static int patchlist_maxpage;
120 static int patchlist_maxsel;
121
122 static int patchlist_pending;
123
124 static void clear_user_area()
125 {
126         int x, y;
127         
128         for(y=OSD_CORNER;y<(OSD_H-OSD_CORNER);y++)
129                 for(x=OSD_CORNER;x<OSD_MAX_USER_X;x++)
130                         osd_fb[x+y*OSD_W] = 0;
131 }
132
133 static void draw_user_area()
134 {
135         int i;
136         int h;
137         int nel;
138         
139         clear_user_area();
140         h = font_get_height(&osd_font);
141         font_draw_string(&osd_font, OSD_CORNER, OSD_CORNER, 0, current_patch);
142         nel = patchlist_page < patchlist_maxpage ? 4 : patchlist_maxsel;
143         for(i=0;i<nel;i++)
144                 font_draw_string(&osd_font, OSD_CORNER+20, OSD_CORNER+(i+1)*h, i == patchlist_sel, patchlist_titles[patchlist_page*4+i]);
145         flush_bridge_cache();
146 }
147
148 static void start_patch_from_list(int n)
149 {
150         strcpy(current_patch, patchlist_titles[n]);
151         patchlist_pending = n;
152 }
153
154 static void process_keys(unsigned int keys)
155 {
156         if(keys & GPIO_BTN1) {
157                 if(patchlist_sel > 0)
158                         patchlist_sel--;
159                 else if(patchlist_page > 0) {
160                         patchlist_page--;
161                         patchlist_sel = 3;
162                 }
163         }
164         if(keys & GPIO_BTN3) {
165                 if(patchlist_page < patchlist_maxpage) {
166                         if(patchlist_sel < 3)
167                                 patchlist_sel++;
168                         else {
169                                 patchlist_page++;
170                                 patchlist_sel = 0;
171                         }
172                 } else if(patchlist_sel < (patchlist_maxsel-1))
173                         patchlist_sel++;
174         }
175         if(keys & GPIO_BTN2)
176                 start_patch_from_list(patchlist_page*4+patchlist_sel);
177
178         draw_user_area();
179 }
180
181 static int lscb(const char *filename, const char *longname, void *param)
182 {
183         char *c;
184
185         if(strlen(longname) < 5) return 1;
186         c = (char *)longname + strlen(longname) - 5;
187         if(strcmp(c, ".milk") != 0) return 1;
188         strcpy(patchlist_filenames[patchlist_n], filename);
189         strncpy(patchlist_titles[patchlist_n], longname, MAX_PATCH_TITLE);
190         patchlist_titles[patchlist_n][MAX_PATCH_TITLE] = 0;
191         c = patchlist_titles[patchlist_n] + strlen(patchlist_titles[patchlist_n]) - 5;
192         if(strcmp(c, ".milk") == 0) *c = 0;
193         patchlist_n++;
194         return patchlist_n < MAX_PATCHES;
195 }
196
197 static void init_ui()
198 {
199         previous_keys = 0;
200         osd_alpha = 0;
201         osd_timer = OSD_DURATION;
202
203         patchlist_n = 0;
204         patchlist_sel = 0;
205         patchlist_page = 0;
206         patchlist_pending = -1;
207
208         if(!fatfs_init(BLOCKDEV_FLASH, 0)) return;
209         fatfs_list_files(lscb, NULL);
210         fatfs_done();
211
212         patchlist_maxpage = (patchlist_n+3)/4 - 1;
213         patchlist_maxsel = patchlist_n % 4;
214         if(patchlist_maxsel == 0)
215                 patchlist_maxsel = 4;
216         
217         if(patchlist_n > 0)
218                 start_patch_from_list(0);
219
220         draw_user_area();
221 }
222
223 void osd_service()
224 {
225         if(patchlist_pending != -1) {
226                 char buffer[8192];
227                 int size;
228                 int n;
229
230                 n = patchlist_pending;
231                 patchlist_pending = -1;
232                 if(!fatfs_init(BLOCKDEV_FLASH, 0)) return;
233                 if(!fatfs_load(patchlist_filenames[n], buffer, sizeof(buffer), &size)) return;
234                 fatfs_done();
235                 buffer[size] = 0;
236
237                 renderer_start(buffer);
238         }
239 }
240
241 int osd_fill_blit_td(struct tmu_td *td, tmu_callback callback, void *user)
242 {
243         unsigned int keys;
244         unsigned int new_keys;
245
246         keys = CSR_GPIO_IN & (GPIO_BTN1|GPIO_BTN2|GPIO_BTN3);
247         new_keys = keys & ~previous_keys;
248         previous_keys = keys;
249
250         if(new_keys) {
251                 osd_timer = OSD_DURATION;
252                 if(osd_alpha != 0)
253                         process_keys(new_keys);
254         }
255
256         osd_timer--;
257         if(osd_timer > 0) {
258                 osd_alpha += 4;
259                 if(osd_alpha > OSD_MAX_ALPHA)
260                         osd_alpha = OSD_MAX_ALPHA;
261         } else {
262                 osd_alpha--;
263                 if(osd_alpha < 0)
264                         osd_alpha = 0;
265         }
266
267         td->flags = TMU_CTL_CHROMAKEY;
268         td->hmeshlast = 1;
269         td->vmeshlast = 1;
270         td->brightness = TMU_BRIGHTNESS_MAX;
271         td->chromakey = OSD_CHROMAKEY;
272         td->vertices = &osd_vertices[0][0];
273         td->texfbuf = osd_fb;
274         td->texhres = OSD_W;
275         td->texvres = OSD_H;
276         td->texhmask = TMU_MASK_NOFILTER;
277         td->texvmask = TMU_MASK_NOFILTER;
278         td->dstfbuf = vga_backbuffer;
279         td->dsthres = vga_hres;
280         td->dstvres = vga_vres;
281         td->dsthoffset = osd_x;
282         td->dstvoffset = osd_y;
283         td->dstsquarew = OSD_W;
284         td->dstsquareh = OSD_H;
285         td->alpha = osd_alpha;
286         td->callback = callback;
287         td->user = user;
288
289         return osd_alpha != 0;
290 }
291