diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/export/screendump.h | 62 | ||||
-rw-r--r-- | firmware/screendump.c | 371 |
2 files changed, 433 insertions, 0 deletions
diff --git a/firmware/export/screendump.h b/firmware/export/screendump.h new file mode 100644 index 0000000000..515db08a70 --- /dev/null +++ b/firmware/export/screendump.h | |||
@@ -0,0 +1,62 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 by Jens Arnold | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #ifndef __SCREENDUMP_H__ | ||
23 | #define __SCREENDUMP_H__ | ||
24 | |||
25 | #include "config.h" | ||
26 | |||
27 | /* Make BMP colour map entries from R, G, B triples, without and with blending. | ||
28 | * Not within HAVE_LCD_BITMAP because it is also used for the Player sim */ | ||
29 | #define RED_CMP(c) (((c) >> 16) & 0xff) | ||
30 | #define GREEN_CMP(c) (((c) >> 8) & 0xff) | ||
31 | #define BLUE_CMP(c) ((c) & 0xff) | ||
32 | |||
33 | #define BMP_COLOR(c) BLUE_CMP(c), GREEN_CMP(c), RED_CMP(c), 0 | ||
34 | #define BMP_COLOR_MIX(c1, c2, num, den) \ | ||
35 | (BLUE_CMP(c2) - BLUE_CMP(c1)) * (num) / (den) + BLUE_CMP(c1), \ | ||
36 | (GREEN_CMP(c2) - GREEN_CMP(c1)) * (num) / (den) + GREEN_CMP(c1), \ | ||
37 | (RED_CMP(c2) - RED_CMP(c1)) * (num) / (den) + RED_CMP(c1), 0 | ||
38 | |||
39 | #define LE16_CONST(x) (x)&0xff, ((x)>>8)&0xff | ||
40 | #define LE32_CONST(x) (x)&0xff, ((x)>>8)&0xff, ((x)>>16)&0xff, ((x)>>24)&0xff | ||
41 | |||
42 | |||
43 | #ifdef BOOTLOADER | ||
44 | |||
45 | #define screen_dump() | ||
46 | #define remote_screen_dump() | ||
47 | |||
48 | #else /* !BOOTLOADER */ | ||
49 | |||
50 | #ifdef HAVE_LCD_BITMAP | ||
51 | /* Save a .BMP file containing the current screen contents. */ | ||
52 | void screen_dump(void); | ||
53 | void screen_dump_set_hook(void (*hook)(int fd)); | ||
54 | #endif | ||
55 | #ifdef HAVE_REMOTE_LCD | ||
56 | /* Save a .BMP file containing the current remote screen contents. */ | ||
57 | void remote_screen_dump(void); | ||
58 | #endif | ||
59 | |||
60 | #endif /* !BOOTLOADER */ | ||
61 | |||
62 | #endif /* __SCREENDUMP_H__ */ | ||
diff --git a/firmware/screendump.c b/firmware/screendump.c new file mode 100644 index 0000000000..3a3985505e --- /dev/null +++ b/firmware/screendump.c | |||
@@ -0,0 +1,371 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2009 by Jens Arnold | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "config.h" | ||
23 | #include "screendump.h" | ||
24 | |||
25 | #include "file.h" | ||
26 | #include "general.h" | ||
27 | #include "lcd.h" | ||
28 | #include "stdlib.h" | ||
29 | #include "string.h" | ||
30 | #include "system.h" | ||
31 | |||
32 | #ifdef HAVE_REMOTE_LCD | ||
33 | #include "lcd-remote.h" | ||
34 | #endif | ||
35 | |||
36 | #if LCD_DEPTH == 16 | ||
37 | #define BMP_COMPRESSION 3 /* BI_BITFIELDS */ | ||
38 | #define BMP_NUMCOLORS 3 | ||
39 | #else /* LCD_DEPTH != 16 */ | ||
40 | #define BMP_COMPRESSION 0 /* BI_RGB */ | ||
41 | #if LCD_DEPTH <= 8 | ||
42 | #ifdef HAVE_LCD_SPLIT | ||
43 | #define BMP_NUMCOLORS (2 << LCD_DEPTH) | ||
44 | #else | ||
45 | #define BMP_NUMCOLORS (1 << LCD_DEPTH) | ||
46 | #endif | ||
47 | #else /* LCD_DEPTH > 8 */ | ||
48 | #define BMP_NUMCOLORS 0 | ||
49 | #endif /* LCD_DEPTH > 8 */ | ||
50 | #endif /* LCD_DEPTH != 16 */ | ||
51 | |||
52 | #if LCD_DEPTH <= 4 | ||
53 | #define BMP_BPP 4 | ||
54 | #define BMP_LINESIZE ((LCD_WIDTH/2 + 3) & ~3) | ||
55 | #elif LCD_DEPTH <= 8 | ||
56 | #define BMP_BPP 8 | ||
57 | #define BMP_LINESIZE ((LCD_WIDTH + 3) & ~3) | ||
58 | #elif LCD_DEPTH <= 16 | ||
59 | #define BMP_BPP 16 | ||
60 | #define BMP_LINESIZE ((LCD_WIDTH*2 + 3) & ~3) | ||
61 | #else | ||
62 | #define BMP_BPP 24 | ||
63 | #define BMP_LINESIZE ((LCD_WIDTH*3 + 3) & ~3) | ||
64 | #endif | ||
65 | |||
66 | #define BMP_HEADERSIZE (54 + 4 * BMP_NUMCOLORS) | ||
67 | #define BMP_DATASIZE (BMP_LINESIZE * (LCD_HEIGHT+LCD_SPLIT_LINES)) | ||
68 | #define BMP_TOTALSIZE (BMP_HEADERSIZE + BMP_DATASIZE) | ||
69 | |||
70 | static const unsigned char bmpheader[] = | ||
71 | { | ||
72 | 0x42, 0x4d, /* 'BM' */ | ||
73 | LE32_CONST(BMP_TOTALSIZE), /* Total file size */ | ||
74 | 0x00, 0x00, 0x00, 0x00, /* Reserved */ | ||
75 | LE32_CONST(BMP_HEADERSIZE), /* Offset to start of pixel data */ | ||
76 | |||
77 | 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */ | ||
78 | LE32_CONST(LCD_WIDTH), /* Width in pixels */ | ||
79 | LE32_CONST(LCD_HEIGHT+LCD_SPLIT_LINES), /* Height in pixels */ | ||
80 | 0x01, 0x00, /* Number of planes (always 1) */ | ||
81 | LE16_CONST(BMP_BPP), /* Bits per pixel 1/4/8/16/24 */ | ||
82 | LE32_CONST(BMP_COMPRESSION),/* Compression mode */ | ||
83 | LE32_CONST(BMP_DATASIZE), /* Size of bitmap data */ | ||
84 | 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */ | ||
85 | 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */ | ||
86 | LE32_CONST(BMP_NUMCOLORS), /* Number of used colours */ | ||
87 | LE32_CONST(BMP_NUMCOLORS), /* Number of important colours */ | ||
88 | |||
89 | #if LCD_DEPTH == 1 | ||
90 | #ifdef HAVE_NEGATIVE_LCD | ||
91 | BMP_COLOR(LCD_BL_DARKCOLOR), | ||
92 | BMP_COLOR(LCD_BL_BRIGHTCOLOR), | ||
93 | #ifdef HAVE_LCD_SPLIT | ||
94 | BMP_COLOR(LCD_BL_DARKCOLOR_2), | ||
95 | BMP_COLOR(LCD_BL_BRIGHTCOLOR_2), | ||
96 | #endif | ||
97 | #else /* positive display */ | ||
98 | BMP_COLOR(LCD_BL_BRIGHTCOLOR), | ||
99 | BMP_COLOR(LCD_BL_DARKCOLOR), | ||
100 | #endif /* positive display */ | ||
101 | #elif LCD_DEPTH == 2 | ||
102 | BMP_COLOR(LCD_BL_BRIGHTCOLOR), | ||
103 | BMP_COLOR_MIX(LCD_BL_BRIGHTCOLOR, LCD_BL_DARKCOLOR, 1, 3), | ||
104 | BMP_COLOR_MIX(LCD_BL_BRIGHTCOLOR, LCD_BL_DARKCOLOR, 2, 3), | ||
105 | BMP_COLOR(LCD_BL_DARKCOLOR), | ||
106 | #elif LCD_DEPTH == 16 | ||
107 | 0x00, 0xf8, 0x00, 0x00, /* red bitfield mask */ | ||
108 | 0xe0, 0x07, 0x00, 0x00, /* green bitfield mask */ | ||
109 | 0x1f, 0x00, 0x00, 0x00, /* blue bitfield mask */ | ||
110 | #endif | ||
111 | }; | ||
112 | |||
113 | static void (*screen_dump_hook)(int fh) = NULL; | ||
114 | |||
115 | void screen_dump(void) | ||
116 | { | ||
117 | int fd, y; | ||
118 | char filename[MAX_PATH]; | ||
119 | |||
120 | fb_data *src; | ||
121 | #if LCD_DEPTH == 1 | ||
122 | unsigned mask; | ||
123 | unsigned val; | ||
124 | #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT != HORIZONTAL_PACKING) | ||
125 | int shift; | ||
126 | unsigned val; | ||
127 | #endif | ||
128 | #if LCD_DEPTH <= 8 | ||
129 | unsigned char *dst, *dst_end; | ||
130 | unsigned char linebuf[BMP_LINESIZE]; | ||
131 | #elif LCD_DEPTH <= 16 | ||
132 | unsigned short *dst, *dst_end; | ||
133 | unsigned short linebuf[BMP_LINESIZE/2]; | ||
134 | #endif | ||
135 | |||
136 | #if CONFIG_RTC | ||
137 | create_datetime_filename(filename, "", "dump ", ".bmp", false); | ||
138 | #else | ||
139 | create_numbered_filename(filename, "", "dump_", ".bmp", 4 | ||
140 | IF_CNFN_NUM_(, NULL)); | ||
141 | #endif | ||
142 | |||
143 | fd = creat(filename); | ||
144 | if (fd < 0) | ||
145 | return; | ||
146 | |||
147 | if (screen_dump_hook) | ||
148 | { | ||
149 | screen_dump_hook(fd); | ||
150 | } | ||
151 | else | ||
152 | { | ||
153 | write(fd, bmpheader, sizeof(bmpheader)); | ||
154 | |||
155 | /* BMP image goes bottom up */ | ||
156 | for (y = LCD_HEIGHT - 1; y >= 0; y--) | ||
157 | { | ||
158 | memset(linebuf, 0, BMP_LINESIZE); | ||
159 | |||
160 | #if defined(HAVE_LCD_SPLIT) && (LCD_SPLIT_LINES == 2) | ||
161 | if (y == LCD_SPLIT_POS - 1) | ||
162 | { | ||
163 | write(fd, linebuf, BMP_LINESIZE); | ||
164 | write(fd, linebuf, BMP_LINESIZE); | ||
165 | } | ||
166 | #endif | ||
167 | dst = linebuf; | ||
168 | |||
169 | #if LCD_DEPTH == 1 | ||
170 | dst_end = dst + LCD_WIDTH/2; | ||
171 | src = lcd_framebuffer[y >> 3]; | ||
172 | mask = 1 << (y & 7); | ||
173 | |||
174 | do | ||
175 | { | ||
176 | val = (*src++ & mask) ? 0x10 : 0; | ||
177 | val |= (*src++ & mask) ? 0x01 : 0; | ||
178 | #ifdef HAVE_LCD_SPLIT | ||
179 | if (y < LCD_SPLIT_POS) | ||
180 | val |= 0x22; | ||
181 | #endif | ||
182 | *dst++ = val; | ||
183 | } | ||
184 | while (dst < dst_end); | ||
185 | |||
186 | #elif LCD_DEPTH == 2 | ||
187 | dst_end = dst + LCD_WIDTH/2; | ||
188 | |||
189 | #if LCD_PIXELFORMAT == HORIZONTAL_PACKING | ||
190 | src = lcd_framebuffer[y]; | ||
191 | |||
192 | do | ||
193 | { | ||
194 | unsigned data = *src++; | ||
195 | |||
196 | *dst++ = ((data >> 2) & 0x30) | ((data >> 4) & 0x03); | ||
197 | *dst++ = ((data << 2) & 0x30) | (data & 0x03); | ||
198 | } | ||
199 | while (dst < dst_end); | ||
200 | |||
201 | #elif LCD_PIXELFORMAT == VERTICAL_PACKING | ||
202 | src = lcd_framebuffer[y >> 2]; | ||
203 | shift = 2 * (y & 3); | ||
204 | |||
205 | do | ||
206 | { | ||
207 | val = ((*src++ >> shift) & 3) << 4; | ||
208 | val |= ((*src++ >> shift) & 3); | ||
209 | *dst++ = val; | ||
210 | } | ||
211 | while (dst < dst_end); | ||
212 | |||
213 | #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED | ||
214 | src = lcd_framebuffer[y >> 3]; | ||
215 | shift = y & 7; | ||
216 | |||
217 | do | ||
218 | { | ||
219 | unsigned data = (*src++ >> shift) & 0x0101; | ||
220 | |||
221 | val = (((data >> 7) | data) & 3) << 4; | ||
222 | data = (*src++ >> shift) & 0x0101; | ||
223 | val |= ((data >> 7) | data) & 3; | ||
224 | *dst++ = val; | ||
225 | } | ||
226 | while (dst < dst_end); | ||
227 | |||
228 | #endif | ||
229 | #elif LCD_DEPTH == 16 | ||
230 | dst_end = dst + LCD_WIDTH; | ||
231 | src = lcd_framebuffer[y]; | ||
232 | |||
233 | do | ||
234 | { | ||
235 | #if (LCD_PIXELFORMAT == RGB565SWAPPED) | ||
236 | /* iPod LCD data is big endian although the CPU is not */ | ||
237 | *dst++ = htobe16(*src++); | ||
238 | #else | ||
239 | *dst++ = htole16(*src++); | ||
240 | #endif | ||
241 | } | ||
242 | while (dst < dst_end); | ||
243 | |||
244 | #endif /* LCD_DEPTH */ | ||
245 | write(fd, linebuf, BMP_LINESIZE); | ||
246 | } | ||
247 | } | ||
248 | close(fd); | ||
249 | } | ||
250 | |||
251 | void screen_dump_set_hook(void (*hook)(int fh)) | ||
252 | { | ||
253 | screen_dump_hook = hook; | ||
254 | } | ||
255 | |||
256 | #ifdef HAVE_REMOTE_LCD | ||
257 | |||
258 | #define RBMP_COMPRESSION 0 /* BI_RGB */ | ||
259 | #define RBMP_NUMCOLORS (1 << LCD_REMOTE_DEPTH) | ||
260 | #define RBMP_BPP 4 | ||
261 | #define RBMP_LINESIZE ((LCD_REMOTE_WIDTH/2 + 3) & ~3) | ||
262 | |||
263 | #define RBMP_HEADERSIZE (54 + 4 * RBMP_NUMCOLORS) | ||
264 | #define RBMP_DATASIZE (RBMP_LINESIZE * LCD_REMOTE_HEIGHT) | ||
265 | #define RBMP_TOTALSIZE (RBMP_HEADERSIZE + RBMP_DATASIZE) | ||
266 | |||
267 | static const unsigned char rbmpheader[] = | ||
268 | { | ||
269 | 0x42, 0x4d, /* 'BM' */ | ||
270 | LE32_CONST(RBMP_TOTALSIZE), /* Total file size */ | ||
271 | 0x00, 0x00, 0x00, 0x00, /* Reserved */ | ||
272 | LE32_CONST(RBMP_HEADERSIZE), /* Offset to start of pixel data */ | ||
273 | |||
274 | 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */ | ||
275 | LE32_CONST(LCD_REMOTE_WIDTH), /* Width in pixels */ | ||
276 | LE32_CONST(LCD_REMOTE_HEIGHT), /* Height in pixels */ | ||
277 | 0x01, 0x00, /* Number of planes (always 1) */ | ||
278 | LE16_CONST(RBMP_BPP), /* Bits per pixel 1/4/8/16/24 */ | ||
279 | LE32_CONST(RBMP_COMPRESSION), /* Compression mode */ | ||
280 | LE32_CONST(RBMP_DATASIZE), /* Size of bitmap data */ | ||
281 | 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */ | ||
282 | 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */ | ||
283 | LE32_CONST(RBMP_NUMCOLORS), /* Number of used colours */ | ||
284 | LE32_CONST(RBMP_NUMCOLORS), /* Number of important colours */ | ||
285 | |||
286 | #if LCD_REMOTE_DEPTH == 1 | ||
287 | BMP_COLOR(LCD_REMOTE_BL_BRIGHTCOLOR), | ||
288 | BMP_COLOR(LCD_REMOTE_BL_DARKCOLOR), | ||
289 | #elif LCD_REMOTE_DEPTH == 2 | ||
290 | BMP_COLOR(LCD_REMOTE_BL_BRIGHTCOLOR), | ||
291 | BMP_COLOR_MIX(LCD_REMOTE_BL_BRIGHTCOLOR, LCD_REMOTE_BL_DARKCOLOR, 1, 3), | ||
292 | BMP_COLOR_MIX(LCD_REMOTE_BL_BRIGHTCOLOR, LCD_REMOTE_BL_DARKCOLOR, 2, 3), | ||
293 | BMP_COLOR(LCD_REMOTE_BL_DARKCOLOR), | ||
294 | #endif | ||
295 | }; | ||
296 | |||
297 | void remote_screen_dump(void) | ||
298 | { | ||
299 | int fd, y; | ||
300 | char filename[MAX_PATH]; | ||
301 | |||
302 | fb_remote_data *src; | ||
303 | #if LCD_REMOTE_DEPTH == 1 | ||
304 | unsigned mask; | ||
305 | unsigned val; | ||
306 | #elif LCD_REMOTE_DEPTH == 2 | ||
307 | int shift; | ||
308 | unsigned val; | ||
309 | #endif | ||
310 | unsigned char *dst, *dst_end; | ||
311 | unsigned char linebuf[RBMP_LINESIZE]; | ||
312 | |||
313 | #if CONFIG_RTC | ||
314 | create_datetime_filename(filename, "", "rdump ", ".bmp", false); | ||
315 | #else | ||
316 | create_numbered_filename(filename, "", "rdump_", ".bmp", 4 | ||
317 | IF_CNFN_NUM_(, NULL)); | ||
318 | #endif | ||
319 | |||
320 | fd = creat(filename); | ||
321 | if (fd < 0) | ||
322 | return; | ||
323 | |||
324 | write(fd, rbmpheader, sizeof(rbmpheader)); | ||
325 | |||
326 | /* BMP image goes bottom up */ | ||
327 | for (y = LCD_REMOTE_HEIGHT - 1; y >= 0; y--) | ||
328 | { | ||
329 | memset(linebuf, 0, RBMP_LINESIZE); | ||
330 | |||
331 | dst = linebuf; | ||
332 | |||
333 | #if LCD_REMOTE_DEPTH == 1 | ||
334 | dst_end = dst + LCD_REMOTE_WIDTH/2; | ||
335 | src = lcd_remote_framebuffer[y >> 3]; | ||
336 | mask = 1 << (y & 7); | ||
337 | |||
338 | do | ||
339 | { | ||
340 | val = (*src++ & mask) ? 0x10 : 0; | ||
341 | val |= (*src++ & mask) ? 0x01 : 0; | ||
342 | *dst++ = val; | ||
343 | } | ||
344 | while (dst < dst_end); | ||
345 | |||
346 | #elif LCD_REMOTE_DEPTH == 2 | ||
347 | dst_end = dst + LCD_REMOTE_WIDTH/2; | ||
348 | |||
349 | #if LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED | ||
350 | src = lcd_remote_framebuffer[y >> 3]; | ||
351 | shift = y & 7; | ||
352 | |||
353 | do | ||
354 | { | ||
355 | unsigned data = (*src++ >> shift) & 0x0101; | ||
356 | |||
357 | val = (((data >> 7) | data) & 3) << 4; | ||
358 | data = (*src++ >> shift) & 0x0101; | ||
359 | val |= ((data >> 7) | data) & 3; | ||
360 | *dst++ = val; | ||
361 | } | ||
362 | while (dst < dst_end); | ||
363 | |||
364 | #endif | ||
365 | #endif /* LCD_REMOTE_DEPTH */ | ||
366 | write(fd, linebuf, RBMP_LINESIZE); | ||
367 | } | ||
368 | close(fd); | ||
369 | } | ||
370 | |||
371 | #endif /* HAVE_REMOTE_LCD */ | ||