Annotation of /trunk/busybox/patches/busybox-1.15.3-fbsplash-tykef-1.0.patch
Parent Directory | Revision Log
Revision 1044 -
(hide annotations)
(download)
Sat May 8 10:56:35 2010 UTC (14 years, 4 months ago) by niro
File size: 55144 byte(s)
Sat May 8 10:56:35 2010 UTC (14 years, 4 months ago) by niro
File size: 55144 byte(s)
-adds some advanced features to fbslash
1 | niro | 1044 | *** busybox-1.15.0/include/usage.h.orig 2009-08-22 14:52:14.000000000 +0200 |
2 | --- busybox-1.15.0/include/usage.h 2009-09-01 08:42:18.000000000 +0200 | ||
3 | *************** | ||
4 | *** 156,162 **** | ||
5 | "\n -n Start new tone" \ | ||
6 | |||
7 | #define fbsplash_trivial_usage \ | ||
8 | ! "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD]" | ||
9 | #define fbsplash_full_usage "\n\n" \ | ||
10 | "Options:\n" \ | ||
11 | "\n -s Image" \ | ||
12 | --- 156,163 ---- | ||
13 | "\n -n Start new tone" \ | ||
14 | |||
15 | #define fbsplash_trivial_usage \ | ||
16 | ! "-s IMGFILE [-c] [-d DEV] [-i INIFILE] [-f CMD] " \ | ||
17 | ! IF_FEATURE_FBSPLASH_FONTLOAD( "[-m FONTMAPFILE]" ) | ||
18 | #define fbsplash_full_usage "\n\n" \ | ||
19 | "Options:\n" \ | ||
20 | "\n -s Image" \ | ||
21 | *************** | ||
22 | *** 165,172 **** | ||
23 | --- 166,178 ---- | ||
24 | "\n -i Config file (var=value):" \ | ||
25 | "\n BAR_LEFT,BAR_TOP,BAR_WIDTH,BAR_HEIGHT" \ | ||
26 | "\n BAR_R,BAR_G,BAR_B" \ | ||
27 | + "\n TEXT_LEFT,TEXT_TOP,TEXT_R,TEXT_G,TEXT_B,TEXT_SIZE" \ | ||
28 | "\n -f Control pipe (else exit after drawing image)" \ | ||
29 | "\n commands: 'NN' (% for progress bar) or 'exit'" \ | ||
30 | + IF_FEATURE_FBSPLASH_FONTLOAD( \ | ||
31 | + "\n -m Font map file" \ | ||
32 | + "\n commands: 'NN' (% for progress bar), 'write:string to print' or 'exit'" \ | ||
33 | + ) | ||
34 | |||
35 | #define brctl_trivial_usage \ | ||
36 | "COMMAND [BRIDGE [INTERFACE]]" | ||
37 | *** busybox-1.15.0/miscutils/Config.in.orig 2009-08-22 17:59:16.000000000 +0200 | ||
38 | --- busybox-1.15.0/miscutils/Config.in 2009-09-01 08:44:41.000000000 +0200 | ||
39 | *************** | ||
40 | *** 256,262 **** | ||
41 | default n | ||
42 | help | ||
43 | Shows splash image and progress bar on framebuffer device. | ||
44 | ! Can be used during boot phase of an embedded device. ~2kb. | ||
45 | Usage: | ||
46 | - use kernel option 'vga=xxx' or otherwise enable fb device. | ||
47 | - put somewhere fbsplash.cfg file and an image in .ppm format. | ||
48 | --- 256,263 ---- | ||
49 | default n | ||
50 | help | ||
51 | Shows splash image and progress bar on framebuffer device. | ||
52 | ! Can be used during boot phase of an embedded device. | ||
53 | ! Code size is 2..7kb depends on enabled features. | ||
54 | Usage: | ||
55 | - use kernel option 'vga=xxx' or otherwise enable fb device. | ||
56 | - put somewhere fbsplash.cfg file and an image in .ppm format. | ||
57 | *************** | ||
58 | *** 272,277 **** | ||
59 | --- 273,316 ---- | ||
60 | "NN" (ASCII decimal number) - percentage to show on progress bar | ||
61 | "exit" - well you guessed it | ||
62 | |||
63 | + config FEATURE_FBSPLASH_8BPP | ||
64 | + bool "support 8bit depth with color mapping" | ||
65 | + depends on FBSPLASH | ||
66 | + help | ||
67 | + support for low color framebuffer that's need color mapping ~350 b | ||
68 | + | ||
69 | + config FEATURE_FBSPLASH_SPRITES | ||
70 | + bool "Render icons and animations" | ||
71 | + default n | ||
72 | + depends on FBSPLASH | ||
73 | + help | ||
74 | + This option adds the ability to display icons on the | ||
75 | + image displayed by the fbsplash applet. ~1.5kb. | ||
76 | + - command for fifo: | ||
77 | + "load:x:y:filename" - load images from file | ||
78 | + each sprite is one square area when image is wider than higher | ||
79 | + before load new sprite set, old area is cleared. | ||
80 | + "sprite:n" - show one sprite - sprite 0 is background. | ||
81 | + "anime:n:m:t" - anime from sprite n up/down to sprite m | ||
82 | + time between sprites is in t * 100 msec. | ||
83 | + | ||
84 | + config FEATURE_FBSPLASH_TEXT | ||
85 | + bool "text rendering" | ||
86 | + default n | ||
87 | + depends on FBSPLASH | ||
88 | + help | ||
89 | + This option adds the ability to print text messages on the | ||
90 | + image displayed by the fbsplash applet. ~2kb. | ||
91 | + - command for fifo: | ||
92 | + "write:string to print" - print the string after the word "write:" | ||
93 | + | ||
94 | + config FEATURE_FBSPLASH_FONTLOAD | ||
95 | + bool "font loading" | ||
96 | + depends on FEATURE_FBSPLASH_TEXT | ||
97 | + help | ||
98 | + The font is loaded from a standard console font file. | ||
99 | + Supported are psf v1.x and psf v2.0 files including mapping ~1kb. | ||
100 | + | ||
101 | config FLASH_LOCK | ||
102 | bool "flash_lock" | ||
103 | default n | ||
104 | *** busybox-1.15.0/miscutils/fbsplash.c.orig 2009-08-21 00:26:14.000000000 +0200 | ||
105 | --- busybox-1.15.0/miscutils/fbsplash.c 2009-09-01 08:42:18.000000000 +0200 | ||
106 | *************** | ||
107 | *** 1,6 **** | ||
108 | --- 1,7 ---- | ||
109 | /* vi: set sw=4 ts=4: */ | ||
110 | /* | ||
111 | * Copyright (C) 2008 Michele Sanges <michele.sanges@gmail.com> | ||
112 | + * Copyright (C) 2009 Jiri Gabriel <george.gabriel@seznam.cz> | ||
113 | * | ||
114 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. | ||
115 | * | ||
116 | *************** | ||
117 | *** 10,51 **** | ||
118 | * - run applet: $ setsid fbsplash [params] & | ||
119 | * -c: hide cursor | ||
120 | * -d /dev/fbN: framebuffer device (if not /dev/fb0) | ||
121 | ! * -s path_to_image_file (can be "-" for stdin) | ||
122 | * -i path_to_cfg_file | ||
123 | * -f path_to_fifo (can be "-" for stdin) | ||
124 | * - if you want to run it only in presence of a kernel parameter | ||
125 | * (for example fbsplash=on), use: | ||
126 | * grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] | ||
127 | * - commands for fifo: | ||
128 | * "NN" (ASCII decimal number) - percentage to show on progress bar. | ||
129 | ! * "exit" (or just close fifo) - well you guessed it. | ||
130 | */ | ||
131 | |||
132 | #include "libbb.h" | ||
133 | #include <linux/fb.h> | ||
134 | |||
135 | /* If you want logging messages on /tmp/fbsplash.log... */ | ||
136 | #define DEBUG 0 | ||
137 | |||
138 | ! #define BYTES_PER_PIXEL 2 | ||
139 | |||
140 | ! typedef unsigned short DATA; | ||
141 | |||
142 | struct globals { | ||
143 | #if DEBUG | ||
144 | bool bdebug_messages; // enable/disable logging | ||
145 | FILE *logfile_fd; // log file | ||
146 | #endif | ||
147 | ! unsigned char *addr; // pointer to framebuffer memory | ||
148 | ! unsigned ns[7]; // n-parameters | ||
149 | const char *image_filename; | ||
150 | struct fb_var_screeninfo scr_var; | ||
151 | struct fb_fix_screeninfo scr_fix; | ||
152 | }; | ||
153 | #define G (*ptr_to_globals) | ||
154 | #define INIT_G() do { \ | ||
155 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
156 | } while (0) | ||
157 | |||
158 | #define nbar_width ns[0] // progress bar width | ||
159 | #define nbar_height ns[1] // progress bar height | ||
160 | --- 11,198 ---- | ||
161 | * - run applet: $ setsid fbsplash [params] & | ||
162 | * -c: hide cursor | ||
163 | * -d /dev/fbN: framebuffer device (if not /dev/fb0) | ||
164 | ! * -s path_to_image_file - ppm or ppm.gz or ppm.bz2 | ||
165 | * -i path_to_cfg_file | ||
166 | * -f path_to_fifo (can be "-" for stdin) | ||
167 | + * -m font map file - standard psf file (v1 and v2) can be with unumap | ||
168 | * - if you want to run it only in presence of a kernel parameter | ||
169 | * (for example fbsplash=on), use: | ||
170 | * grep -q "fbsplash=on" </proc/cmdline && setsid fbsplash [params] | ||
171 | * - commands for fifo: | ||
172 | * "NN" (ASCII decimal number) - percentage to show on progress bar. | ||
173 | ! * if number is smaller than zero progress bar is filled from left. | ||
174 | ! * "write:string to print" - print the string after the word "write:" | ||
175 | ! * "exit" - well you guessed it. | ||
176 | ! */ | ||
177 | ! | ||
178 | ! /* | ||
179 | ! * enhancements: | ||
180 | ! * - framebuffer can be in depth 8,15,16,24,32 PSEUDOCOLR,TRUECOLOR | ||
181 | ! * not only 16 | ||
182 | ! * - image file cannot be stdin | ||
183 | ! * - bugfix: if virtual size is bigger than visual resolution | ||
184 | ! * - progress bar can be dscending for shutdown (-1..-100) | ||
185 | ! * - progress bar is from background picture + color in cfg - only colorize. | ||
186 | ! * - bouncing bar - command "bounce" and at start. | ||
187 | ! * - smaller image is shown centered on screen cfg positions recalc on demand. | ||
188 | ! * - without text rendering and sprites grow size is only 800b. | ||
189 | ! * text rendering: | ||
190 | ! * - font is standard psf console font | ||
191 | ! * - if font not specified or not compiled in fontload function | ||
192 | ! * font is used from kernel using KDFONTOP / GIO_FONTX / GIO_FONT | ||
193 | ! * - colorize text render by \033[30m .. \033[37m,\033[1;30 .. \033[1;37m | ||
194 | ! * sprites: | ||
195 | ! * - animated sprites load by fifo command "load:x:y:filename". | ||
196 | ! * File is ppm format may be gzipped/bzipped or raw. | ||
197 | ! * Frame/Icon is always square. Width of each Frame is image height. | ||
198 | ! * Frame number 0 is always created for save background. | ||
199 | ! * Transparent color is in ppm file is stored as #FF00FF - purple. | ||
200 | ! * Show frame by pipe command "sprite:n" where n is frame number. | ||
201 | ! * Run animated sequence by "anime:n:m:t" where | ||
202 | ! * n is first frame m is last frame, t is time between displaying | ||
203 | ! * frames in 0.1sec. n can be bigger than m for reverse order in anime. | ||
204 | */ | ||
205 | |||
206 | #include "libbb.h" | ||
207 | + #if BB_BIG_ENDIAN | ||
208 | + #warning "fbsplash work only on little endian architecures. Sorry" | ||
209 | + int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
210 | + int fbsplash_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) { | ||
211 | + bb_error_msg_and_die("fbsplash not work on big endian architectures."); | ||
212 | + return 1; | ||
213 | + } | ||
214 | + | ||
215 | + #else | ||
216 | #include <linux/fb.h> | ||
217 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
218 | + #include <linux/kd.h> | ||
219 | + | ||
220 | + /* if you have fonts with sequences can turn this to 1 i not find any font with this feature | ||
221 | + ~510 bytes of code */ | ||
222 | + #define ENABLE_FEATURE_FBSPLASH_PSFSEQS 0 | ||
223 | + | ||
224 | + /* if you not use text colors and need optimize size - disable text colors | ||
225 | + ~250 bytes of code */ | ||
226 | + #define ENABLE_FEATURE_FBSPLASH_TEXT_COLORS 1 | ||
227 | + | ||
228 | + #if !defined(GIO_UNIMAP) | ||
229 | + struct unipair { | ||
230 | + unsigned short unicode; | ||
231 | + unsigned short fontpos; | ||
232 | + } __atribute__ ((packed)); | ||
233 | + | ||
234 | + struct unimapdesc { | ||
235 | + unsigned short entry_ct; | ||
236 | + struct unipair *entries; | ||
237 | + } __atribute__ ((packed)); | ||
238 | + #endif | ||
239 | + | ||
240 | + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD | ||
241 | + | ||
242 | + enum { // uint16 | ||
243 | + PSF1_MAGIC = 0x0436, | ||
244 | + PSF1_SEPARATOR = 0xFFFF, | ||
245 | + PSF1_STARTSEQ = 0xFFFE | ||
246 | + }; | ||
247 | + | ||
248 | + enum { // uint32 | ||
249 | + PSF2_MAGIC = 0x864ab572, | ||
250 | + PSF2_HAS_UNICODE_TABLE = 0x01, /* bits used in flags */ | ||
251 | + PSF2_MAXVERSION = 0 /* max version recognized so far */ | ||
252 | + }; | ||
253 | + | ||
254 | + enum { // uint8 | ||
255 | + PSF1_MODE512 = 0x01, | ||
256 | + PSF1_MODEHASTAB = 0x02, | ||
257 | + PSF1_MODEHASSEQ = 0x04, | ||
258 | + PSF1_MAXMODE = 0x05, | ||
259 | + PSF2_STARTSEQ = 0xFE, | ||
260 | + PSF2_SEPARATOR = 0xFF | ||
261 | + }; | ||
262 | + | ||
263 | + struct psf1_header { | ||
264 | + uint16_t magic; /* Magic number */ | ||
265 | + uint8_t mode; /* PSF font mode */ | ||
266 | + uint8_t charsize; /* Character size */ | ||
267 | + } __attribute__ ((packed)); | ||
268 | + | ||
269 | + struct psf2_header { | ||
270 | + uint32_t magic; | ||
271 | + uint32_t version; | ||
272 | + uint32_t headersize; /* offset of bitmaps in file */ | ||
273 | + uint32_t flags; | ||
274 | + uint32_t length; /* number of glyphs */ | ||
275 | + uint32_t charsize; /* number of bytes for each character */ | ||
276 | + uint32_t height, width; /* max dimensions of glyphs */ | ||
277 | + /* charsize = height * ((width + 7) / 8) */ | ||
278 | + } __attribute__ ((packed)); | ||
279 | + | ||
280 | + #endif // FONTLOAD | ||
281 | + struct fbfont { | ||
282 | + uint8_t *mem1; | ||
283 | + uint8_t *glyphs; | ||
284 | + struct unimapdesc ud; | ||
285 | + int height; | ||
286 | + int width; | ||
287 | + int glyphcount; | ||
288 | + int charsize; | ||
289 | + int rowbytes; | ||
290 | + }; | ||
291 | + | ||
292 | + #endif | ||
293 | |||
294 | /* If you want logging messages on /tmp/fbsplash.log... */ | ||
295 | #define DEBUG 0 | ||
296 | |||
297 | ! typedef uint32_t RGB; | ||
298 | |||
299 | ! struct sprite { | ||
300 | ! int w,h,bw; // width height in pixels, width in bytes | ||
301 | ! uint8_t pixbuf[0]; | ||
302 | ! }; | ||
303 | ! | ||
304 | ! struct ppmfile { | ||
305 | ! int fd; | ||
306 | ! int sizx,sizy; | ||
307 | ! int bufhead,buftail; | ||
308 | ! uint8_t buf[0]; // buf for entire pixels row | ||
309 | ! }; | ||
310 | |||
311 | struct globals { | ||
312 | #if DEBUG | ||
313 | bool bdebug_messages; // enable/disable logging | ||
314 | FILE *logfile_fd; // log file | ||
315 | #endif | ||
316 | ! unsigned char *addr; // pointer to visible framebuffer memory | ||
317 | ! unsigned char *mapaddr; // pointer to framebuffer memory | ||
318 | ! #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
319 | ! #define FBCFGVALS 13 | ||
320 | ! #else | ||
321 | ! #define FBCFGVALS 7 | ||
322 | ! #endif | ||
323 | ! unsigned ns[FBCFGVALS]; // n-parameters | ||
324 | const char *image_filename; | ||
325 | struct fb_var_screeninfo scr_var; | ||
326 | struct fb_fix_screeninfo scr_fix; | ||
327 | + int bytes_per_pixel,x0,y0; // offset for centering | ||
328 | + struct sprite *spb1,*spb2; | ||
329 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
330 | + struct fbfont *font; | ||
331 | + struct sprite *spt; | ||
332 | + uint8_t *unknown_glyph; | ||
333 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
334 | + uint8_t combglyph[4*32]; /* max width=32 max height=32 */ | ||
335 | + #endif | ||
336 | + #endif | ||
337 | }; | ||
338 | + | ||
339 | #define G (*ptr_to_globals) | ||
340 | #define INIT_G() do { \ | ||
341 | SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \ | ||
342 | } while (0) | ||
343 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
344 | + #define GF (*(ptr_to_globals->font)) | ||
345 | + #endif | ||
346 | |||
347 | #define nbar_width ns[0] // progress bar width | ||
348 | #define nbar_height ns[1] // progress bar height | ||
349 | *************** | ||
350 | *** 54,59 **** | ||
351 | --- 201,275 ---- | ||
352 | #define nbar_colr ns[4] // progress bar color red component | ||
353 | #define nbar_colg ns[5] // progress bar color green component | ||
354 | #define nbar_colb ns[6] // progress bar color blue component | ||
355 | + #define text_posx ns[7] // text horizontal position | ||
356 | + #define text_posy ns[8] // text vertical position | ||
357 | + #define text_colr ns[9] // text color red component | ||
358 | + #define text_colg ns[10] // text color green component | ||
359 | + #define text_colb ns[11] // text color blue component | ||
360 | + #define text_size ns[12] // text size (1 to 4) | ||
361 | + | ||
362 | + /** | ||
363 | + * A function to compute pixel value | ||
364 | + */ | ||
365 | + static RGB FAST_FUNC makecol(uint16_t r, uint16_t g, uint16_t b) { | ||
366 | + if (r > 255) r = 255; | ||
367 | + if (g > 255) g = 255; | ||
368 | + if (b > 255) b = 255; | ||
369 | + #if ENABLE_FEATURE_FBSPLASH_8BPP | ||
370 | + if (G.bytes_per_pixel == 1) { | ||
371 | + // mymap is 6*8*5 = 240 values on offset 16 | ||
372 | + // (r/42.6666) + (g/32) + (b/51.2) | ||
373 | + return 16 + (((r << 8) + r) / 13107) * 40 + | ||
374 | + (((g << 8) + g) / 9362) * 5 + | ||
375 | + (((b << 8) + b) / 16383); | ||
376 | + } | ||
377 | + #endif | ||
378 | + return ((r >> (8 - G.scr_var.red.length )) << G.scr_var.red.offset) | | ||
379 | + ((g >> (8 - G.scr_var.green.length)) << G.scr_var.green.offset) | | ||
380 | + ((b >> (8 - G.scr_var.blue.length )) << G.scr_var.blue.offset); | ||
381 | + } | ||
382 | + | ||
383 | + /* | ||
384 | + * A function to put the pixel row | ||
385 | + */ | ||
386 | + static void fb_putpixrow(int x, int y, int w, uint8_t *rgb) { | ||
387 | + uint8_t *a; | ||
388 | + int realw; | ||
389 | + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres) return; | ||
390 | + a = G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; | ||
391 | + realw = ((x + w) < G.scr_var.xres ? w : G.scr_var.xres - x) * G.bytes_per_pixel; | ||
392 | + memcpy(a,rgb,realw); | ||
393 | + return; | ||
394 | + } | ||
395 | + | ||
396 | + /* | ||
397 | + * A function to put the sprite to position or fill sprite from videoram | ||
398 | + */ | ||
399 | + static void fb_sprite(int x, int y, int x0, int width, struct sprite *s, int direction) { | ||
400 | + uint8_t *a,*p; | ||
401 | + int realw,iy; | ||
402 | + | ||
403 | + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres || | ||
404 | + x0 >= s->w || x0 < 0 || width > (s->w - x0)) { | ||
405 | + return; | ||
406 | + } | ||
407 | + realw = ((x + width) < G.scr_var.xres ? width : (G.scr_var.xres - x)) * G.bytes_per_pixel; | ||
408 | + a = G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; | ||
409 | + p = &(s->pixbuf[x0 * G.bytes_per_pixel]); | ||
410 | + for (iy = 0; iy < s->h; iy++) { | ||
411 | + if ((iy + y) > G.scr_var.yres) break; | ||
412 | + if (direction == 1) { | ||
413 | + memcpy(p,a,realw); | ||
414 | + } else { | ||
415 | + memcpy(a,p,realw); | ||
416 | + } | ||
417 | + a += G.scr_fix.line_length; | ||
418 | + p += s->bw; | ||
419 | + } | ||
420 | + return; | ||
421 | + } | ||
422 | + #define fb_putsprite(x,y,s) fb_sprite(x,y,0,s->w,s,0) | ||
423 | + #define fb_getsprite(x,y,s) fb_sprite(x,y,0,s->w,s,1) | ||
424 | |||
425 | #if DEBUG | ||
426 | #define DEBUG_MESSAGE(strMessage, args...) \ | ||
427 | *************** | ||
428 | *** 78,231 **** | ||
429 | xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var); | ||
430 | xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix); | ||
431 | |||
432 | ! if (G.scr_var.bits_per_pixel != 16) | ||
433 | ! bb_error_msg_and_die("only 16 bpp is supported"); | ||
434 | |||
435 | // map the device in memory | ||
436 | ! G.addr = mmap(NULL, | ||
437 | ! G.scr_var.xres * G.scr_var.yres | ||
438 | ! * BYTES_PER_PIXEL /*(G.scr_var.bits_per_pixel / 8)*/ , | ||
439 | PROT_WRITE, MAP_SHARED, fbfd, 0); | ||
440 | ! if (G.addr == MAP_FAILED) | ||
441 | bb_perror_msg_and_die("mmap"); | ||
442 | close(fbfd); | ||
443 | } | ||
444 | |||
445 | |||
446 | /** | ||
447 | - * Draw hollow rectangle on framebuffer | ||
448 | - */ | ||
449 | - static void fb_drawrectangle(void) | ||
450 | - { | ||
451 | - int cnt; | ||
452 | - DATA thispix; | ||
453 | - DATA *ptr1, *ptr2; | ||
454 | - unsigned char nred = G.nbar_colr/2; | ||
455 | - unsigned char ngreen = G.nbar_colg/2; | ||
456 | - unsigned char nblue = G.nbar_colb/2; | ||
457 | - | ||
458 | - nred >>= 3; // 5-bit red | ||
459 | - ngreen >>= 2; // 6-bit green | ||
460 | - nblue >>= 3; // 5-bit blue | ||
461 | - thispix = nblue + (ngreen << 5) + (nred << (5+6)); | ||
462 | - | ||
463 | - // horizontal lines | ||
464 | - ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); | ||
465 | - ptr2 = (DATA*)(G.addr + ((G.nbar_posy + G.nbar_height - 1) * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); | ||
466 | - cnt = G.nbar_width - 1; | ||
467 | - do { | ||
468 | - *ptr1++ = thispix; | ||
469 | - *ptr2++ = thispix; | ||
470 | - } while (--cnt >= 0); | ||
471 | - | ||
472 | - // vertical lines | ||
473 | - ptr1 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx) * BYTES_PER_PIXEL); | ||
474 | - ptr2 = (DATA*)(G.addr + (G.nbar_posy * G.scr_var.xres + G.nbar_posx + G.nbar_width - 1) * BYTES_PER_PIXEL); | ||
475 | - cnt = G.nbar_height - 1 /* HUH?! G.nbar_posy + G.nbar_height - 1 - G.nbar_posy*/; | ||
476 | - do { | ||
477 | - *ptr1 = thispix; ptr1 += G.scr_var.xres; | ||
478 | - *ptr2 = thispix; ptr2 += G.scr_var.xres; | ||
479 | - } while (--cnt >= 0); | ||
480 | - } | ||
481 | - | ||
482 | - | ||
483 | - /** | ||
484 | - * Draw filled rectangle on framebuffer | ||
485 | - * \param nx1pos,ny1pos upper left position | ||
486 | - * \param nx2pos,ny2pos down right position | ||
487 | - * \param nred,ngreen,nblue rgb color | ||
488 | - */ | ||
489 | - static void fb_drawfullrectangle(int nx1pos, int ny1pos, int nx2pos, int ny2pos, | ||
490 | - unsigned char nred, unsigned char ngreen, unsigned char nblue) | ||
491 | - { | ||
492 | - int cnt1, cnt2, nypos; | ||
493 | - DATA thispix; | ||
494 | - DATA *ptr; | ||
495 | - | ||
496 | - nred >>= 3; // 5-bit red | ||
497 | - ngreen >>= 2; // 6-bit green | ||
498 | - nblue >>= 3; // 5-bit blue | ||
499 | - thispix = nblue + (ngreen << 5) + (nred << (5+6)); | ||
500 | - | ||
501 | - cnt1 = ny2pos - ny1pos; | ||
502 | - nypos = ny1pos; | ||
503 | - do { | ||
504 | - ptr = (DATA*)(G.addr + (nypos * G.scr_var.xres + nx1pos) * BYTES_PER_PIXEL); | ||
505 | - cnt2 = nx2pos - nx1pos; | ||
506 | - do { | ||
507 | - *ptr++ = thispix; | ||
508 | - } while (--cnt2 >= 0); | ||
509 | - | ||
510 | - nypos++; | ||
511 | - } while (--cnt1 >= 0); | ||
512 | - } | ||
513 | - | ||
514 | - | ||
515 | - /** | ||
516 | * Draw a progress bar on framebuffer | ||
517 | ! * \param percent percentage of loading | ||
518 | */ | ||
519 | ! static void fb_drawprogressbar(unsigned percent) | ||
520 | { | ||
521 | ! int i, left_x, top_y, width, height; | ||
522 | |||
523 | ! // outer box | ||
524 | ! left_x = G.nbar_posx; | ||
525 | ! top_y = G.nbar_posy; | ||
526 | ! width = G.nbar_width - 1; | ||
527 | ! height = G.nbar_height - 1; | ||
528 | ! if ((height | width) < 0) | ||
529 | return; | ||
530 | ! // NB: "width" of 1 actually makes rect with width of 2! | ||
531 | ! fb_drawrectangle(); | ||
532 | ! | ||
533 | ! // inner "empty" rectangle | ||
534 | ! left_x++; | ||
535 | ! top_y++; | ||
536 | ! width -= 2; | ||
537 | ! height -= 2; | ||
538 | ! if ((height | width) < 0) | ||
539 | return; | ||
540 | ! fb_drawfullrectangle( | ||
541 | ! left_x, top_y, | ||
542 | ! left_x + width, top_y + height, | ||
543 | ! G.nbar_colr, G.nbar_colg, G.nbar_colb); | ||
544 | ! | ||
545 | ! if (percent > 0) { | ||
546 | ! // actual progress bar | ||
547 | ! width = width * percent / 100; | ||
548 | ! i = height; | ||
549 | ! if (height == 0) | ||
550 | ! height++; // divide by 0 is bad | ||
551 | ! while (i >= 0) { | ||
552 | ! // draw one-line thick "rectangle" | ||
553 | ! // top line will have gray lvl 200, bottom one 100 | ||
554 | ! unsigned gray_level = 100 + i*100/height; | ||
555 | ! fb_drawfullrectangle( | ||
556 | ! left_x, top_y, left_x + width, top_y, | ||
557 | ! gray_level, gray_level, gray_level); | ||
558 | ! top_y++; | ||
559 | ! i--; | ||
560 | ! } | ||
561 | ! } | ||
562 | } | ||
563 | |||
564 | |||
565 | /** | ||
566 | ! * Draw image from PPM file | ||
567 | */ | ||
568 | ! static void fb_drawimage(void) | ||
569 | ! { | ||
570 | ! char *head, *ptr; | ||
571 | ! FILE *theme_file; | ||
572 | ! unsigned char *pixline; | ||
573 | ! unsigned i, j, width, height, line_size; | ||
574 | ! | ||
575 | ! theme_file = xfopen_stdin(G.image_filename); | ||
576 | ! head = xmalloc(256); | ||
577 | ! | ||
578 | /* parse ppm header | ||
579 | ! * - A ppm image’s magic number is the two characters "P6". | ||
580 | * - Whitespace (blanks, TABs, CRs, LFs). | ||
581 | * - A width, formatted as ASCII characters in decimal. | ||
582 | * - Whitespace. | ||
583 | --- 294,411 ---- | ||
584 | xioctl(fbfd, FBIOGET_VSCREENINFO, &G.scr_var); | ||
585 | xioctl(fbfd, FBIOGET_FSCREENINFO, &G.scr_fix); | ||
586 | |||
587 | ! #if ENABLE_FEATURE_FBSPLASH_8BPP | ||
588 | ! if ((G.scr_var.bits_per_pixel < 8) || | ||
589 | ! (G.scr_fix.type != FB_TYPE_PACKED_PIXELS)) { | ||
590 | ! bb_error_msg_and_die("only 8,15,16,24 and 32 bpp is supported"); | ||
591 | ! } | ||
592 | ! #else | ||
593 | ! if ((G.scr_var.bits_per_pixel < 15) || | ||
594 | ! (G.scr_fix.type != FB_TYPE_PACKED_PIXELS)) { | ||
595 | ! bb_error_msg_and_die("only 15,16,24 and 32 bpp is supported"); | ||
596 | ! } | ||
597 | ! #endif | ||
598 | ! switch (G.scr_fix.visual) { | ||
599 | ! #if ENABLE_FEATURE_FBSPLASH_8BPP | ||
600 | ! case FB_VISUAL_PSEUDOCOLOR: do { | ||
601 | ! uint8_t *cmem; | ||
602 | ! struct fb_cmap *cmap; | ||
603 | ! int r,g,b,i; | ||
604 | ! cmem = xzalloc(sizeof(struct fb_cmap) + sizeof(uint16_t) * 3 * 256); | ||
605 | ! cmap = (struct fb_cmap *)cmem; | ||
606 | ! cmap->len = 256; | ||
607 | ! cmap->red = (uint16_t *)&(cmem[sizeof(struct fb_cmap)]); | ||
608 | ! cmap->green = &(cmap->red[256]); | ||
609 | ! cmap->blue = &(cmap->green[256]); | ||
610 | ! xioctl(fbfd, FBIOGETCMAP, cmem); // read original palette | ||
611 | ! // fill cmap with my 6x8x5 pallette | ||
612 | ! for (r = 0,i = 16; r < 6; r++) { | ||
613 | ! for (g = 0; g < 8; g++) { | ||
614 | ! for (b = 0; b < 5; b++, i++) { | ||
615 | ! cmap->red[i] = r * 13107; | ||
616 | ! cmap->green[i] = g * 9362; | ||
617 | ! cmap->blue[i] = b * 16383; | ||
618 | ! } | ||
619 | ! } | ||
620 | ! } | ||
621 | ! xioctl(fbfd, FBIOPUTCMAP, cmem); | ||
622 | ! free(cmem); | ||
623 | ! } while(0); | ||
624 | ! #endif | ||
625 | ! case FB_VISUAL_TRUECOLOR: | ||
626 | ! break; | ||
627 | ! default: | ||
628 | ! bb_error_msg_and_die("only TrueColor modes is supported"); | ||
629 | ! } | ||
630 | |||
631 | + G.bytes_per_pixel = (G.scr_var.bits_per_pixel + 7) / 8; | ||
632 | // map the device in memory | ||
633 | ! G.mapaddr = mmap(NULL, | ||
634 | ! G.scr_var.yres_virtual * G.scr_fix.line_length , | ||
635 | PROT_WRITE, MAP_SHARED, fbfd, 0); | ||
636 | ! if (G.mapaddr == MAP_FAILED) | ||
637 | bb_perror_msg_and_die("mmap"); | ||
638 | + G.addr = G.mapaddr + G.scr_var.yoffset * G.scr_fix.line_length; | ||
639 | close(fbfd); | ||
640 | } | ||
641 | |||
642 | |||
643 | /** | ||
644 | * Draw a progress bar on framebuffer | ||
645 | ! * \param frompct percentage on which bar starting | ||
646 | ! * \param topct percentage where bar end | ||
647 | */ | ||
648 | ! static void fb_drawnbar(int frompct, int topct) | ||
649 | { | ||
650 | ! int w0,w1,w2; | ||
651 | |||
652 | ! if (G.spb1 == NULL || G.spb2 == NULL) | ||
653 | return; | ||
654 | ! if (frompct < 0 || frompct > 100 || | ||
655 | ! topct < 0 || topct > 100 || | ||
656 | ! frompct > topct) { | ||
657 | return; | ||
658 | ! } | ||
659 | ! w0 = G.spb1->w * frompct / 100; | ||
660 | ! w0 = (w0 > G.spb1->w ? G.spb1->w : w0); | ||
661 | ! w2 = G.spb1->w * (100 - topct) / 100; | ||
662 | ! w2 = ((w0 + w2) > G.spb1->w ? G.spb1->w - w0 : w2); | ||
663 | ! w1 = G.spb1->w - w0 - w2; | ||
664 | ! if (w0 > 0) | ||
665 | ! fb_sprite(G.nbar_posx, G.nbar_posy, 0, w0, G.spb1, 0); // left "empty" area | ||
666 | ! if (w1 > 0) | ||
667 | ! fb_sprite(G.nbar_posx + w0, G.nbar_posy, w0, w1, G.spb2, 0); // filled area | ||
668 | ! if (w2 > 0) | ||
669 | ! fb_sprite(G.nbar_posx + w0 + w1, G.nbar_posy, w0 + w1, w2, G.spb1, 0); // right "empty" area | ||
670 | } | ||
671 | |||
672 | + static struct sprite *fb_newsprite(int pw, int ph, int scrx, int scry) { | ||
673 | + struct sprite *s; | ||
674 | + s=xmalloc(sizeof(struct sprite) + | ||
675 | + ph * pw * G.bytes_per_pixel); | ||
676 | + s->w = pw; | ||
677 | + s->h = ph; | ||
678 | + s->bw = pw * G.bytes_per_pixel; | ||
679 | + if (scrx >= 0) fb_getsprite(scrx, scry, s); // read | ||
680 | + return s; | ||
681 | + } | ||
682 | |||
683 | /** | ||
684 | ! * Open PPM file - must be binary, color maxval 255 | ||
685 | */ | ||
686 | ! static struct ppmfile *ppm_open(const char *filename) { | ||
687 | ! int i,k,l,val; | ||
688 | ! struct ppmfile *p; | ||
689 | ! uint8_t *s; | ||
690 | ! char *e; | ||
691 | ! | ||
692 | ! i = open_zipped(filename); | ||
693 | ! if (i == -1) return NULL; | ||
694 | ! p = (struct ppmfile *) xmalloc(sizeof(struct ppmfile) + 600); | ||
695 | ! // initial buf 600 bytes header cannot be larger | ||
696 | ! p->fd = i; | ||
697 | /* parse ppm header | ||
698 | ! * - A ppm images magic number is the two characters "P6". | ||
699 | * - Whitespace (blanks, TABs, CRs, LFs). | ||
700 | * - A width, formatted as ASCII characters in decimal. | ||
701 | * - Whitespace. | ||
702 | *************** | ||
703 | *** 237,292 **** | ||
704 | * - A raster of Width * Height pixels in triplets of rgb | ||
705 | * in pure binary by 1 (or not implemented 2) bytes. | ||
706 | */ | ||
707 | ! while (1) { | ||
708 | ! if (fgets(head, 256, theme_file) == NULL | ||
709 | ! /* do not overrun the buffer */ | ||
710 | ! || strlen(bb_common_bufsiz1) >= sizeof(bb_common_bufsiz1) - 256) | ||
711 | ! bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); | ||
712 | ! | ||
713 | ! ptr = memchr(skip_whitespace(head), '#', 256); | ||
714 | ! if (ptr != NULL) | ||
715 | ! *ptr = 0; /* ignore comments */ | ||
716 | ! strcat(bb_common_bufsiz1, head); | ||
717 | ! // width, height, max_color_val | ||
718 | ! if (sscanf(bb_common_bufsiz1, "P6 %u %u %u", &width, &height, &i) == 3 | ||
719 | ! && i <= 255) | ||
720 | ! break; | ||
721 | ! /* If we do not find a signature throughout the whole file then | ||
722 | ! we will diagnose this via EOF on read in the head of the loop. */ | ||
723 | } | ||
724 | |||
725 | ! if (ENABLE_FEATURE_CLEAN_UP) | ||
726 | ! free(head); | ||
727 | ! if (width != G.scr_var.xres || height != G.scr_var.yres) | ||
728 | bb_error_msg_and_die("PPM %dx%d does not match screen %dx%d", | ||
729 | ! width, height, G.scr_var.xres, G.scr_var.yres); | ||
730 | ! line_size = width*3; | ||
731 | if (width > G.scr_var.xres) | ||
732 | width = G.scr_var.xres; | ||
733 | if (height > G.scr_var.yres) | ||
734 | height = G.scr_var.yres; | ||
735 | |||
736 | ! pixline = xmalloc(line_size); | ||
737 | ! for (j = 0; j < height; j++) { | ||
738 | ! unsigned char *pixel = pixline; | ||
739 | ! DATA *src = (DATA *)(G.addr + j * G.scr_fix.line_length); | ||
740 | |||
741 | ! if (fread(pixline, 1, line_size, theme_file) != line_size) | ||
742 | ! bb_error_msg_and_die("bad PPM file '%s'", G.image_filename); | ||
743 | for (i = 0; i < width; i++) { | ||
744 | ! unsigned thispix; | ||
745 | ! thispix = (((unsigned)pixel[0] << 8) & 0xf800) | ||
746 | ! | (((unsigned)pixel[1] << 3) & 0x07e0) | ||
747 | ! | (((unsigned)pixel[2] >> 3)); | ||
748 | ! *src++ = thispix; | ||
749 | pixel += 3; | ||
750 | } | ||
751 | } | ||
752 | ! if (ENABLE_FEATURE_CLEAN_UP) | ||
753 | ! free(pixline); | ||
754 | ! fclose(theme_file); | ||
755 | } | ||
756 | |||
757 | |||
758 | /** | ||
759 | * Parse configuration file | ||
760 | --- 417,1159 ---- | ||
761 | * - A raster of Width * Height pixels in triplets of rgb | ||
762 | * in pure binary by 1 (or not implemented 2) bytes. | ||
763 | */ | ||
764 | ! l=read(p->fd,&(p->buf[0]),600); | ||
765 | ! if ((l < 0) || (p->buf[0] != 'P') || (p->buf[1] != '6')) { | ||
766 | ! goto ppmopen_end; | ||
767 | ! } | ||
768 | ! for (s = &(p->buf[2]), i = 0, k = 0; s < &(p->buf[l]) && (k < 3); ) { | ||
769 | ! switch (i) { | ||
770 | ! case 0: | ||
771 | ! if (*s <= ' ') { | ||
772 | ! s++; // skip whitespaces | ||
773 | ! } else { | ||
774 | ! i++; // next state | ||
775 | ! } | ||
776 | ! break; | ||
777 | ! case 1: // num or rmks | ||
778 | ! if (*s == '#') { | ||
779 | ! i = 2; | ||
780 | ! } else { | ||
781 | ! val=bb_strtoi((char *)s,&e,10); | ||
782 | ! if (s == (uint8_t *)e) goto ppmopen_end; // no number - error | ||
783 | ! s=(uint8_t *)e; | ||
784 | ! i=0; // skip whitespaces | ||
785 | ! switch (++k) { | ||
786 | ! case 1: p->sizx = val; break; | ||
787 | ! case 2: p->sizy = val; break; | ||
788 | ! case 3: | ||
789 | ! if (val > 255) | ||
790 | ! goto ppmopen_end; // only 1 byte per color | ||
791 | ! s++; | ||
792 | ! p->bufhead = s - &(p->buf[0]); | ||
793 | ! p->buftail = l; | ||
794 | ! break; | ||
795 | ! } | ||
796 | ! } | ||
797 | ! break; | ||
798 | ! case 2: // skip to eol | ||
799 | ! if (*s == 10) i=0; | ||
800 | ! s++; | ||
801 | ! break; | ||
802 | ! } | ||
803 | ! } | ||
804 | ! if (p->sizx < 1 || p->sizy < 1) goto ppmopen_end; | ||
805 | ! if (p->sizx > 200) { | ||
806 | ! // buf is only 600 bytes = 200 pix prealocated - need more. | ||
807 | ! p = xrealloc(p,sizeof(struct ppmfile) + 3 * p->sizx); | ||
808 | } | ||
809 | + return p; | ||
810 | |||
811 | ! ppmopen_end: | ||
812 | ! close(p->fd); | ||
813 | ! free(p); | ||
814 | ! return NULL; | ||
815 | ! } | ||
816 | ! | ||
817 | ! /** | ||
818 | ! * Read pixelline from ppm to internal buffer and return pointer | ||
819 | ! */ | ||
820 | ! static uint8_t *ppm_readline(struct ppmfile *p) { | ||
821 | ! int i; | ||
822 | ! if (p==NULL) return NULL; | ||
823 | ! if (p->bufhead < p->buftail) { | ||
824 | ! // pack buffer | ||
825 | ! for (i = 0; p->bufhead < p->buftail; i++) { | ||
826 | ! p->buf[i]=p->buf[p->bufhead++]; | ||
827 | ! } | ||
828 | ! p->buftail = i; | ||
829 | ! } else { | ||
830 | ! p->buftail = 0; | ||
831 | ! } | ||
832 | ! while (p->buftail < (p->sizx * 3)) { | ||
833 | ! i=read(p->fd, &(p->buf[p->buftail]), (p->sizx * 3) - p->buftail); | ||
834 | ! if (i < 0) return NULL; | ||
835 | ! p->buftail += i; | ||
836 | ! } | ||
837 | ! p->bufhead = p->sizx * 3; | ||
838 | ! return &(p->buf[0]); | ||
839 | ! } | ||
840 | ! | ||
841 | ! /** | ||
842 | ! * Close ppmfile | ||
843 | ! */ | ||
844 | ! | ||
845 | ! static void ppm_close(struct ppmfile *p) { | ||
846 | ! if (p == NULL) return; | ||
847 | ! close(p->fd); | ||
848 | ! free(p); | ||
849 | ! } | ||
850 | ! | ||
851 | ! /** | ||
852 | ! * Draws a PPM image. | ||
853 | ! */ | ||
854 | ! static void fb_drawimage(void) | ||
855 | ! { | ||
856 | ! struct ppmfile *ppm; | ||
857 | ! unsigned char *pixel; | ||
858 | ! unsigned char *rgbline,*rgbpix,*sprpix; | ||
859 | ! RGB thispix; | ||
860 | ! unsigned i, j, width, height; | ||
861 | ! | ||
862 | ! ppm = ppm_open(G.image_filename); | ||
863 | ! if (ppm == NULL) | ||
864 | ! bb_error_msg_and_die("PPM file %s cannot be opened",G.image_filename); | ||
865 | ! width = ppm->sizx; | ||
866 | ! height = ppm->sizy; | ||
867 | ! if (width > G.scr_var.xres || height > G.scr_var.yres) | ||
868 | bb_error_msg_and_die("PPM %dx%d does not match screen %dx%d", | ||
869 | ! width, height, G.scr_var.xres, G.scr_var.yres); | ||
870 | if (width > G.scr_var.xres) | ||
871 | width = G.scr_var.xres; | ||
872 | if (height > G.scr_var.yres) | ||
873 | height = G.scr_var.yres; | ||
874 | |||
875 | ! G.x0 = (width == G.scr_var.xres ? 0 : (G.scr_var.xres - width) / 2); | ||
876 | ! G.y0 = (height == G.scr_var.yres ? 0 : (G.scr_var.yres - height) / 2); | ||
877 | ! rgbline = xmalloc((width * G.bytes_per_pixel) + sizeof(RGB)); | ||
878 | |||
879 | ! for (j = 0; j < height; j++) { | ||
880 | ! pixel = ppm_readline(ppm); | ||
881 | ! rgbpix = rgbline; | ||
882 | ! if (pixel == NULL) | ||
883 | ! bb_error_msg_and_die("bad PPM file '%s' line %d", G.image_filename,j); | ||
884 | ! if (G.spb2 != NULL && j >= G.nbar_posy && j < (G.nbar_posy + G.spb2->h)) { | ||
885 | ! sprpix = &(G.spb2->pixbuf[(j - G.nbar_posy) * G.spb2->bw]); | ||
886 | ! } else { | ||
887 | ! sprpix = NULL; | ||
888 | ! } | ||
889 | for (i = 0; i < width; i++) { | ||
890 | ! thispix = makecol(pixel[0],pixel[1],pixel[2]); | ||
891 | ! *((RGB*)rgbpix) = thispix; | ||
892 | ! if ((sprpix != NULL) && (i >= G.nbar_posx)) { | ||
893 | ! if (G.nbar_colr >= 256) { // Multiple not add | ||
894 | ! thispix = makecol((G.nbar_colr & 255) * pixel[0], (G.nbar_colg & 255) * pixel[1], (G.nbar_colb & 255) * pixel[2]); | ||
895 | ! } else { | ||
896 | ! thispix = makecol(G.nbar_colr + pixel[0], G.nbar_colg + pixel[1], G.nbar_colb + pixel[2]); | ||
897 | ! } | ||
898 | ! memcpy(sprpix,&thispix,G.bytes_per_pixel); | ||
899 | ! if (i >= (G.nbar_posx + G.spb2->w - 1)) { | ||
900 | ! sprpix = NULL; | ||
901 | ! } else { | ||
902 | ! sprpix += G.bytes_per_pixel; | ||
903 | ! } | ||
904 | ! } | ||
905 | pixel += 3; | ||
906 | + rgbpix += G.bytes_per_pixel; | ||
907 | + } | ||
908 | + fb_putpixrow(G.x0,j + G.y0,width,rgbline); | ||
909 | + } | ||
910 | + free(rgbline); | ||
911 | + ppm_close(ppm); | ||
912 | + // recalc cfg if centered image | ||
913 | + G.nbar_posx += G.x0; | ||
914 | + G.nbar_posy += G.y0; | ||
915 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
916 | + G.text_posx += G.x0; | ||
917 | + G.text_posy += G.y0; | ||
918 | + #endif | ||
919 | + } | ||
920 | + | ||
921 | + #if ENABLE_FEATURE_FBSPLASH_SPRITES | ||
922 | + /** | ||
923 | + * Read rectangle sprites from ppm file | ||
924 | + * \return pointer to array of sprites | ||
925 | + * \return number of sprites loaded | ||
926 | + */ | ||
927 | + static struct sprite *fb_loadsprites(char *filename, int sx, int sy) { | ||
928 | + struct ppmfile *ppm; | ||
929 | + uint8_t *pixel, *rgbpix; | ||
930 | + struct sprite *s=NULL; | ||
931 | + int y,i; | ||
932 | + RGB thispix; | ||
933 | + | ||
934 | + if ((ppm = ppm_open(filename)) == NULL) goto readsprites_out; | ||
935 | + s = fb_newsprite(ppm->sizx + ppm->sizy, ppm->sizy, -1, -1); | ||
936 | + fb_sprite(sx + G.x0, sy + G.y0, 0, s->h, s, 1); // load square 0 - background | ||
937 | + rgbpix = &(s->pixbuf[0]); | ||
938 | + for (y = 0; y < ppm->sizy; y++) { | ||
939 | + // skip square 0 | ||
940 | + rgbpix += s->h * G.bytes_per_pixel; | ||
941 | + // read from file | ||
942 | + pixel = ppm_readline(ppm); | ||
943 | + for (i = 0; (pixel != NULL) && (i < ppm->sizx); i++) { | ||
944 | + if (pixel[0]==0xFF && pixel[1]==0 && pixel[2]==0xFF) { | ||
945 | + // transparent color => get pixel from square 0 | ||
946 | + memcpy(&thispix, | ||
947 | + &(s->pixbuf[y * s->bw + (i % s->h) * G.bytes_per_pixel]), | ||
948 | + G.bytes_per_pixel); | ||
949 | + } else { | ||
950 | + thispix = makecol(pixel[0],pixel[1],pixel[2]); | ||
951 | + } | ||
952 | + memcpy(rgbpix,&thispix,G.bytes_per_pixel); | ||
953 | + pixel += 3; | ||
954 | + rgbpix += G.bytes_per_pixel; | ||
955 | + } | ||
956 | + } | ||
957 | + ppm_close(ppm); | ||
958 | + return s; | ||
959 | + readsprites_out: | ||
960 | + if (s != NULL) free(s); | ||
961 | + return NULL; | ||
962 | + } | ||
963 | + | ||
964 | + #define fb_putanim(x,y,n,s) fb_sprite(x + G.x0,y + G.y0,s->h * n,s->h,s,0) | ||
965 | + #endif | ||
966 | + | ||
967 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
968 | + | ||
969 | + /* | ||
970 | + * A function to put the pixel | ||
971 | + */ | ||
972 | + static void fb_putpixel(int x, int y, RGB rgb) { | ||
973 | + uint8_t *a; | ||
974 | + if (x < 0 || y < 0 || x > G.scr_var.xres || y > G.scr_var.yres) return; | ||
975 | + a=G.addr + G.scr_fix.line_length * y + G.bytes_per_pixel * x; | ||
976 | + memcpy(a,&rgb,G.bytes_per_pixel); | ||
977 | + /* | ||
978 | + switch (G.scr_var.bits_per_pixel) { | ||
979 | + case 8: | ||
980 | + *a = rgb & 0xFF; | ||
981 | + break; | ||
982 | + case 15: | ||
983 | + case 16: | ||
984 | + *((uint16_t *)a) = rgb & 0xFFFF; | ||
985 | + break; | ||
986 | + case 24: | ||
987 | + a[0] = rgb & 0xFF; | ||
988 | + a[1] = (rgb >> 8) & 0xFF; | ||
989 | + a[2] = (rgb >> 16) & 0xFF; | ||
990 | + break; | ||
991 | + case 32: | ||
992 | + *((uint32_t *)a) = rgb; | ||
993 | + break; | ||
994 | + } | ||
995 | + */ | ||
996 | + return; | ||
997 | + } | ||
998 | + | ||
999 | + /** | ||
1000 | + * UTF-8 - get one charcter and advance to next | ||
1001 | + * ??? this function may be in libbb ??? | ||
1002 | + * \return UCS4 value | ||
1003 | + */ | ||
1004 | + static uint32_t ucs4value(uint8_t **s) { | ||
1005 | + uint8_t *c; | ||
1006 | + uint32_t ucs,w; | ||
1007 | + int count; | ||
1008 | + | ||
1009 | + if (s==NULL || *s==NULL || **s==0) return 0; | ||
1010 | + c = *s; | ||
1011 | + ucs = *c++; | ||
1012 | + if (ucs >= 0xC0) { /* 0x80..0xBF is error in utf8 but i use as-is */ | ||
1013 | + for (w = ucs & 0x7F, count = 0;(w & 0x40) == 0x40; count++) { | ||
1014 | + w <<= 1; | ||
1015 | + } | ||
1016 | + ucs=ucs & (0x7E >> count); | ||
1017 | + for(; count > 0; count--) { | ||
1018 | + if ((*c & 0xC0) != 0x80) { /* error in utf8 */ | ||
1019 | + ucs=0; | ||
1020 | + break; | ||
1021 | + } | ||
1022 | + ucs = (ucs << 6) + (*c++ & 0x3F); | ||
1023 | + } | ||
1024 | + } | ||
1025 | + *s=c; | ||
1026 | + return ucs; | ||
1027 | + } | ||
1028 | + | ||
1029 | + /** | ||
1030 | + * Find in GF.ud.entries desired UTF-8 char and advance to next char | ||
1031 | + * can combine glyphs in own memory | ||
1032 | + * \param s pointer to utf8 char pointer | ||
1033 | + * \return pinter to glyph | ||
1034 | + */ | ||
1035 | + static uint8_t *getglyph(uint8_t **s) { | ||
1036 | + uint32_t ucs; | ||
1037 | + uint8_t *c,*g; | ||
1038 | + int utflen,i; | ||
1039 | + | ||
1040 | + if (s == NULL || *s == NULL || **s == 0) | ||
1041 | + return G.unknown_glyph; /* end of string - no advance */ | ||
1042 | + c = *s; | ||
1043 | + ucs = ucs4value(&c); | ||
1044 | + utflen = c - *s; | ||
1045 | + g = G.unknown_glyph; | ||
1046 | + if (GF.ud.entries == NULL) { | ||
1047 | + if (ucs < GF.glyphcount) { | ||
1048 | + g = GF.glyphs + GF.charsize * ucs; | ||
1049 | + } | ||
1050 | + } else if (ucs <= 0xFFFF) { // only ucs2 | ||
1051 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1052 | + int j,compose; | ||
1053 | + memset(G.combglyph,0,sizeof(G.combglyph)); | ||
1054 | + for (i = 0, compose=0; i < GF.ud.entry_ct; i++) { | ||
1055 | + if (GF.ud.entries[i].unicode == ucs) { | ||
1056 | + g=GF.glyphs + | ||
1057 | + GF.ud.entries[i].fontpos * GF.charsize; | ||
1058 | + for (j = 0; j < GF.charsize; j++) { | ||
1059 | + G.combglyph[j] |= g[j]; | ||
1060 | + } | ||
1061 | + compose++; | ||
1062 | + } | ||
1063 | + } | ||
1064 | + if (compose > 1) { // found more than one | ||
1065 | + g = &(G.combglyph[0]); | ||
1066 | + } | ||
1067 | + #else | ||
1068 | + // no sequences - first found win | ||
1069 | + for (i = 0; i < GF.ud.entry_ct ; i++) { | ||
1070 | + if (GF.ud.entries[i].unicode == ucs) { | ||
1071 | + g = GF.glyphs + GF.ud.entries[i].fontpos * GF.charsize; | ||
1072 | + break; | ||
1073 | + } | ||
1074 | + } | ||
1075 | + #endif | ||
1076 | + } | ||
1077 | + *s=c; | ||
1078 | + return g; | ||
1079 | + } | ||
1080 | + | ||
1081 | + /** | ||
1082 | + * Draws a single character on framebuffer. | ||
1083 | + * \param nXPos horizontal position from which draw the character. | ||
1084 | + * \param nYPos vertical position from which draw the character. | ||
1085 | + * \param nCharSize character scale factor [1..4]. | ||
1086 | + * \param pointer to *nChar character to draw - auto advance by utf8. | ||
1087 | + * \param RGB color | ||
1088 | + */ | ||
1089 | + static void fb_drawchar(int nXPos, int nYPos, int nCharSize, | ||
1090 | + uint8_t **nChar, RGB thispix) | ||
1091 | + { | ||
1092 | + int x,y,px; | ||
1093 | + uint8_t *glyph; | ||
1094 | + | ||
1095 | + if (G.font==NULL || GF.height==0 || GF.glyphs==NULL) return; | ||
1096 | + if (G.unknown_glyph == NULL) { | ||
1097 | + uint8_t *unknown; | ||
1098 | + const uint8_t unconst[]="?"; | ||
1099 | + unknown=(uint8_t *)unconst; | ||
1100 | + G.unknown_glyph = getglyph(&unknown); | ||
1101 | + } | ||
1102 | + | ||
1103 | + glyph=getglyph(nChar); | ||
1104 | + if (glyph==NULL) return; | ||
1105 | + glyph-=GF.rowbytes; | ||
1106 | + for (y = 0; y < (GF.height * nCharSize); y++) { | ||
1107 | + if ((y % nCharSize) == 0) glyph+=GF.rowbytes; | ||
1108 | + for (x = 0,px = -1; x < (GF.width * nCharSize); x++) { | ||
1109 | + if ((x % nCharSize) == 0) px++; | ||
1110 | + if ((glyph[px / 8] & (0x80 >> (px % 8))) != 0) { | ||
1111 | + fb_putpixel(nXPos + x, nYPos + y,thispix); | ||
1112 | + } | ||
1113 | + } | ||
1114 | + } | ||
1115 | + } | ||
1116 | + | ||
1117 | + | ||
1118 | + /** | ||
1119 | + * Draws a string on framebuffer. | ||
1120 | + * \param *strString pointer to the string. | ||
1121 | + */ | ||
1122 | + static void fb_drawstring(char *strString) | ||
1123 | + { | ||
1124 | + RGB thispix; | ||
1125 | + int nCharSize; | ||
1126 | + int x; | ||
1127 | + uint8_t *c; | ||
1128 | + | ||
1129 | + // size check | ||
1130 | + nCharSize = G.text_size; | ||
1131 | + if (nCharSize < 1) | ||
1132 | + nCharSize = 1; | ||
1133 | + else if (nCharSize > 4) | ||
1134 | + nCharSize = 4; | ||
1135 | + | ||
1136 | + if (G.font==NULL || GF.height==0 || GF.glyphs==NULL) return; | ||
1137 | + | ||
1138 | + // redraws the portion of image interested to the old write operation | ||
1139 | + fb_putsprite(G.text_posx, G.text_posy, G.spt); | ||
1140 | + | ||
1141 | + // new write operation | ||
1142 | + thispix=makecol(G.text_colr,G.text_colg,G.text_colb); | ||
1143 | + for (x = G.text_posx, c = (uint8_t *)strString; *c != 0;) { | ||
1144 | + #if ENABLE_FEATURE_FBSPLASH_TEXT_COLORS | ||
1145 | + if (*c==0x1b && c[1]=='[') { | ||
1146 | + long newcol=0,first; | ||
1147 | + char *e; | ||
1148 | + e=(char *)&(c[1]); | ||
1149 | + do { | ||
1150 | + first=newcol; | ||
1151 | + ++e; | ||
1152 | + newcol = strtol(e,&e,10); | ||
1153 | + } while (*e == ';'); | ||
1154 | + if (*e == 'm' && ((newcol == 0) || (newcol >= 30 && newcol <= 37))) { | ||
1155 | + if (newcol >= 30 && newcol <= 37) { | ||
1156 | + if (newcol == 0 && first == 1) { | ||
1157 | + thispix=makecol(0x33,0x33,0x33); | ||
1158 | + } else { | ||
1159 | + newcol -= 30; | ||
1160 | + first = (first == 1 ? 0xFF : 0xCC); | ||
1161 | + thispix=makecol((newcol & 1) ? first : 0, | ||
1162 | + (newcol & 2) ? first : 0, | ||
1163 | + (newcol & 4) ? first : 0); | ||
1164 | + } | ||
1165 | + } else { | ||
1166 | + thispix=makecol(G.text_colr,G.text_colg,G.text_colb); | ||
1167 | + } | ||
1168 | + c=(uint8_t *)(++e); | ||
1169 | + continue; | ||
1170 | + } | ||
1171 | + } | ||
1172 | + #endif | ||
1173 | + fb_drawchar(x, G.text_posy, nCharSize, &c, thispix); | ||
1174 | + x += GF.width * nCharSize; | ||
1175 | + if (x >= G.scr_var.xres) break; // too long string | ||
1176 | + } | ||
1177 | + } | ||
1178 | + | ||
1179 | + /** | ||
1180 | + * Free readed font structure with all allocated memory | ||
1181 | + */ | ||
1182 | + static void fb_freefont(void) { | ||
1183 | + if (G.font != NULL) { | ||
1184 | + if (GF.mem1 != NULL) free(GF.mem1); | ||
1185 | + if (GF.ud.entries != NULL) free(GF.ud.entries); | ||
1186 | + free(G.font); | ||
1187 | + G.font=NULL; | ||
1188 | + } | ||
1189 | + } | ||
1190 | + | ||
1191 | + /** | ||
1192 | + * Read font and mapping from console using ioctl GIO_FONT and GIO_UNIMAP | ||
1193 | + */ | ||
1194 | + static void fb_readvgafont(void) { | ||
1195 | + int fd,i; | ||
1196 | + #if (defined(KDFONTOP) && defined(KD_FONT_OP_GET)) || defined(GIO_FONTX) | ||
1197 | + #if defined(KDFONTOP) && defined(KD_FONT_OP_GET) | ||
1198 | + struct console_font_op fo; | ||
1199 | + fd = open(CURRENT_TTY,O_NONBLOCK); | ||
1200 | + // if (fd == -1) fd = open("/dev/tty0",O_NOCTTY); | ||
1201 | + // if (fd == -1) fd = open("/dev/tty1",O_NOCTTY); | ||
1202 | + if (fd == -1) return; | ||
1203 | + | ||
1204 | + fo.data = NULL; | ||
1205 | + fo.charcount = 0; | ||
1206 | + fo.height = 32; fo.width = 32; // max usable font size | ||
1207 | + fo.flags = 0; | ||
1208 | + fo.op = KD_FONT_OP_GET; | ||
1209 | + if (ioctl(fd, KDFONTOP, &fo) == 0) { | ||
1210 | + i = (fo.width + 7) / 8; | ||
1211 | + fo.data = xmalloc(fo.charcount * 32 * i); | ||
1212 | + if (ioctl(fd, KDFONTOP, &fo) == -1) { | ||
1213 | + free(fo.data); | ||
1214 | + goto fb_vgafontout; | ||
1215 | + } | ||
1216 | + } | ||
1217 | + if (G.font != NULL) fb_freefont(); | ||
1218 | + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); | ||
1219 | + GF.height = fo.height; | ||
1220 | + GF.glyphcount = fo.charcount; | ||
1221 | + GF.width = fo.width; | ||
1222 | + GF.rowbytes = (fo.width + 7) / 8; | ||
1223 | + GF.charsize = GF.height * GF.rowbytes; | ||
1224 | + GF.mem1 = (uint8_t *) xmalloc(GF.height * GF.glyphcount * GF.rowbytes); | ||
1225 | + GF.glyphs = GF.mem1; | ||
1226 | + for (i = 0; i < GF.glyphcount; i++) { | ||
1227 | + memcpy(&(GF.glyphs[i * GF.height * GF.rowbytes]), | ||
1228 | + &(fo.data[i * 32 * GF.rowbytes]), | ||
1229 | + GF.height * GF.rowbytes); | ||
1230 | + } | ||
1231 | + if (fo.data) free(fo.data); | ||
1232 | + #else // KDFONTOP / GIO_FONTX | ||
1233 | + struct consolefontdesc cfd; | ||
1234 | + int j; | ||
1235 | + fd = open(CURRENT_TTY,O_NONBLOCK); | ||
1236 | + // if (fd == -1) fd = open("/dev/tty0",O_NOCTTY); | ||
1237 | + // if (fd == -1) fd = open("/dev/tty1",O_NOCTTY); | ||
1238 | + if (fd == -1) return; | ||
1239 | + | ||
1240 | + cfd.chardata = NULL; | ||
1241 | + cfd.charcount = 0; | ||
1242 | + cfd.charheight = 0; | ||
1243 | + if (ioctl(fd, GIO_FONTX, &cfd) == 0) { | ||
1244 | + cfd.chardata = xmalloc(cfd.charcount * 32); | ||
1245 | + if (ioctl(fd, GIO_FONTX, &cfd) == -1) { | ||
1246 | + free(cfd.chardata); | ||
1247 | + goto fb_vgafontout; | ||
1248 | + } | ||
1249 | + } else { | ||
1250 | + cfd.charcount=256; | ||
1251 | + cfd.chardata = xmalloc(256 * 32); | ||
1252 | + if (ioctl(fd, GIO_FONT, cfd.chardata) != 0) { | ||
1253 | + free(cfd.chardata); | ||
1254 | + goto fb_vgafontout; | ||
1255 | + } | ||
1256 | + // compute real height | ||
1257 | + cfd.charheight=0; | ||
1258 | + for (j = 31; (j > 0) && (cfd.charheight == 0); j--) { | ||
1259 | + for (i = 0; (i < 256) && (cfd.charheight == 0); i++) { | ||
1260 | + if (cfd.chardata[i*32+j] != 0) | ||
1261 | + cfd.charheight=j; | ||
1262 | + } | ||
1263 | + } | ||
1264 | + } | ||
1265 | + if (cfd.charheight != 0) { | ||
1266 | + if (G.font != NULL) fb_freefont(); | ||
1267 | + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); | ||
1268 | + GF.charsize = cfd.charheight; | ||
1269 | + GF.height = GF.charsize; | ||
1270 | + GF.glyphcount = cfd.charcount; | ||
1271 | + GF.width = 8; | ||
1272 | + GF.rowbytes = 1; | ||
1273 | + GF.mem1 = (uint8_t *) xmalloc(GF.height * GF.glyphcount); | ||
1274 | + GF.glyphs = GF.mem1; | ||
1275 | + // pack font | ||
1276 | + for (i = 0; i < GF.glyphcount; i++) { | ||
1277 | + memcpy(&(GF.glyphs[i * GF.height]), | ||
1278 | + &(cfd.chardata[i * 32]), | ||
1279 | + GF.height); | ||
1280 | } | ||
1281 | } | ||
1282 | ! free(cfd.chardata); | ||
1283 | ! #endif // FONTOP / FONTX | ||
1284 | ! #if defined(GIO_UNIMAP) | ||
1285 | ! GF.ud.entry_ct = 0; | ||
1286 | ! ioctl(fd, GIO_UNIMAP, &(GF.ud)); | ||
1287 | ! if (errno != ENOMEM) { | ||
1288 | ! goto fb_vgafontout; | ||
1289 | ! } | ||
1290 | ! if (GF.ud.entry_ct > 0) { | ||
1291 | ! GF.ud.entries = xmalloc(GF.ud.entry_ct * sizeof(struct unipair)); | ||
1292 | ! if (ioctl(fd, GIO_UNIMAP, &(GF.ud)) == -1) { | ||
1293 | ! // error no unimap | ||
1294 | ! free(GF.ud.entries); | ||
1295 | ! GF.ud.entry_ct = 0; | ||
1296 | ! } | ||
1297 | ! } | ||
1298 | ! #endif // GIO_UNIMAP | ||
1299 | ! fb_vgafontout: | ||
1300 | ! close(fd); | ||
1301 | ! #endif // GIO_FONTX or KDFONTOP available | ||
1302 | ! return; | ||
1303 | ! } | ||
1304 | ! | ||
1305 | ! #if ENABLE_FEATURE_FBSPLASH_FONTLOAD | ||
1306 | ! | ||
1307 | ! static uint16_t nextuni(int ver, uint8_t *map, int *pos) { | ||
1308 | ! uint16_t rv; | ||
1309 | ! int i; | ||
1310 | ! i=*pos; | ||
1311 | ! if (ver==1) { | ||
1312 | ! rv = map[i + 1]; | ||
1313 | ! rv = (rv << 8) + map[i]; | ||
1314 | ! i += 2; | ||
1315 | ! } else { // ver 2 | ||
1316 | ! if (map[i] == 0xFF) rv = 0xFFFF; | ||
1317 | ! else if (map[i] == 0xFE) rv = 0xFFFE; | ||
1318 | ! else if (map[i] < 0xC0) rv = map[i]; | ||
1319 | ! else if (map[i] < 0xE0) { | ||
1320 | ! rv = map[i] & 0x1F; | ||
1321 | ! if ((map[i + 1] & 0xC0) == 0x80) | ||
1322 | ! rv = (rv << 6) + (map[++i] & 0x3F); | ||
1323 | ! } else if (map[i] < 0xF0) { | ||
1324 | ! rv = map[i] & 0x0F; | ||
1325 | ! if ((map[i + 1] & 0xC0) == 0x80) | ||
1326 | ! rv = (rv << 6) + (map[++i] & 0x3F); | ||
1327 | ! if ((map[i + 1] & 0xC0) == 0x80) | ||
1328 | ! rv = (rv << 6) + (map[++i] & 0x3F); | ||
1329 | ! } else rv = map[i]; | ||
1330 | ! i++; | ||
1331 | ! } | ||
1332 | ! *pos=i; | ||
1333 | ! return rv; | ||
1334 | } | ||
1335 | |||
1336 | + /** | ||
1337 | + * Convert unimap from psf v1 and psf v2 to unimapdesc | ||
1338 | + */ | ||
1339 | + static void convertmap(int ver, uint8_t *map, int mapsize, struct unimapdesc *ud) { | ||
1340 | + int i,j,k,glyph,glycodes; | ||
1341 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1342 | + int l, seqlen=0, *seq=NULL; | ||
1343 | + #endif | ||
1344 | + uint16_t e; | ||
1345 | + ud->entry_ct=0; | ||
1346 | + for (i = 0, glycodes=0; i < mapsize; ) { | ||
1347 | + e = nextuni(ver, map, &i); | ||
1348 | + if (e == PSF1_STARTSEQ) { | ||
1349 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1350 | + seqlen++; | ||
1351 | + #endif | ||
1352 | + e = nextuni(ver, map, &i); | ||
1353 | + while ((e != PSF1_SEPARATOR) && (i < mapsize)) { | ||
1354 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1355 | + ud->entry_ct += glycodes; | ||
1356 | + #endif | ||
1357 | + e = nextuni(ver, map, &i); | ||
1358 | + } | ||
1359 | + glycodes=0; | ||
1360 | + } else if (e == PSF1_SEPARATOR) { | ||
1361 | + ud->entry_ct += glycodes; | ||
1362 | + glycodes=0; | ||
1363 | + } else { | ||
1364 | + glycodes++; | ||
1365 | + } | ||
1366 | + } | ||
1367 | + ud->entries = (struct unipair *) | ||
1368 | + xmalloc(sizeof(struct unimapdesc) * ud->entry_ct); | ||
1369 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1370 | + if (seqlen > 0) { | ||
1371 | + seq = xzalloc(sizeof(int) * (seqlen + 1)); | ||
1372 | + } | ||
1373 | + #endif | ||
1374 | + for (i = 0, glyph = 0, j = 0, k = 0; i < mapsize; ) { | ||
1375 | + e = nextuni(ver, map, &i); | ||
1376 | + if (e == PSF1_STARTSEQ) { | ||
1377 | + e = nextuni(ver, map, &i); | ||
1378 | + k++; // this is seqence - position saved | ||
1379 | + // remove sequence - this is not glyph | ||
1380 | + while (ud->entries[j-1].fontpos == glyph && j > 0) { | ||
1381 | + j--; | ||
1382 | + } | ||
1383 | + // skip chars in seq | ||
1384 | + while ((e != PSF1_SEPARATOR) && (i < mapsize)) { | ||
1385 | + e = nextuni(ver, map, &i); | ||
1386 | + } | ||
1387 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1388 | + if (k < seqlen) seq[k] = i; | ||
1389 | + #endif | ||
1390 | + } else if (e == PSF1_SEPARATOR) { | ||
1391 | + glyph++; | ||
1392 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1393 | + // next may be sequence | ||
1394 | + if (k < seqlen) seq[k] = i; | ||
1395 | + #endif | ||
1396 | + } else if (j < ud->entry_ct) { | ||
1397 | + ud->entries[j].unicode=e; | ||
1398 | + ud->entries[j++].fontpos=glyph; | ||
1399 | + } | ||
1400 | + } | ||
1401 | + #if ENABLE_FEATURE_FBSPLASH_PSFSEQS | ||
1402 | + i = 0; | ||
1403 | + while (i < seqlen) { | ||
1404 | + k = seq[i]; | ||
1405 | + do { | ||
1406 | + e = nextuni(ver, map, &k); | ||
1407 | + } while (e != PSF1_STARTSEQ && e != PSF1_SEPARATOR && k < mapsize); | ||
1408 | + if (e == PSF1_STARTSEQ) { | ||
1409 | + e = nextuni(ver, map, &k); | ||
1410 | + while ((e != PSF1_SEPARATOR) && (k < mapsize)) { | ||
1411 | + // find glyph in table | ||
1412 | + glyph = 0; | ||
1413 | + while ((glyph < j) && (ud->entries[glyph].unicode != e)) { | ||
1414 | + glyph++; | ||
1415 | + } | ||
1416 | + // write glyph to table for all chars | ||
1417 | + if (glyph < j) { // found | ||
1418 | + l = seq[i]; // code pointer | ||
1419 | + e = nextuni(ver, map, &l); | ||
1420 | + while(e != PSF1_STARTSEQ && l < mapsize) { | ||
1421 | + ud->entries[j].unicode=e; | ||
1422 | + ud->entries[j++].fontpos=ud->entries[glyph].fontpos; | ||
1423 | + e = nextuni(ver, map, &l); | ||
1424 | + } | ||
1425 | + } | ||
1426 | + e = nextuni(ver, map, &k); | ||
1427 | + } | ||
1428 | + } | ||
1429 | + i++; | ||
1430 | + } | ||
1431 | + if (seqlen > 0) { | ||
1432 | + // remove unfound glyphs if any | ||
1433 | + ud->entry_ct = j; | ||
1434 | + free(seq); | ||
1435 | + } | ||
1436 | + #endif | ||
1437 | + } | ||
1438 | + | ||
1439 | + /** | ||
1440 | + * Read PSF v1 and PSF v2 fonts to own font structure | ||
1441 | + */ | ||
1442 | + static void fb_readpsf(const char *filename) { | ||
1443 | + unsigned int len; | ||
1444 | + int maplen; | ||
1445 | + uint8_t *buf; | ||
1446 | + struct psf1_header *psf1; | ||
1447 | + struct psf2_header *psf2; | ||
1448 | + | ||
1449 | + if (G.font != NULL) fb_freefont(); | ||
1450 | + len = 48*1024; // can't be larger | ||
1451 | + buf = xmalloc_open_zipped_read_close(filename, &len); | ||
1452 | + if (buf == NULL) { | ||
1453 | + bb_simple_perror_msg_and_die(filename); | ||
1454 | + } | ||
1455 | + if (len < 1024) { | ||
1456 | + goto badfmt; | ||
1457 | + } | ||
1458 | + G.font = (struct fbfont *) xzalloc(sizeof(struct fbfont)); | ||
1459 | + GF.mem1 = buf; | ||
1460 | + psf1 = (struct psf1_header *)buf; | ||
1461 | + psf2 = (struct psf2_header *)buf; | ||
1462 | + if ((psf1->magic == PSF1_MAGIC) && (psf1->mode <= PSF1_MAXMODE)) { | ||
1463 | + GF.glyphs = &(buf[sizeof(struct psf1_header)]); | ||
1464 | + GF.charsize = psf1->charsize; | ||
1465 | + GF.height = GF.charsize; | ||
1466 | + GF.glyphcount = ((psf1->mode & PSF1_MODE512) ? 512 : 256); | ||
1467 | + GF.width = 8; | ||
1468 | + maplen = len - sizeof(struct psf1_header) - | ||
1469 | + (GF.charsize * GF.glyphcount); | ||
1470 | + if ((maplen > 0) && ((psf1->mode & PSF1_MODEHASTAB) != 0)) { | ||
1471 | + convertmap(1,&(buf[len - maplen]),maplen,&(GF.ud)); | ||
1472 | + } else { | ||
1473 | + if (maplen < 0) goto badfmt; | ||
1474 | + } | ||
1475 | + } else if ((psf2->magic == PSF2_MAGIC) && | ||
1476 | + (psf2->version <= PSF2_MAXVERSION)) { | ||
1477 | + GF.glyphs=&(buf[psf2->headersize]); | ||
1478 | + GF.height = psf2->height; | ||
1479 | + GF.width = psf2->width; | ||
1480 | + GF.glyphcount = psf2->length; | ||
1481 | + GF.charsize = psf2->charsize; | ||
1482 | + maplen = len - psf2->headersize - | ||
1483 | + (GF.charsize * GF.glyphcount); | ||
1484 | + if ((maplen > 0) && ((psf2->flags & PSF2_HAS_UNICODE_TABLE) != 0)) { | ||
1485 | + convertmap(2,&(buf[len - maplen]),maplen,&(GF.ud)); | ||
1486 | + } else { | ||
1487 | + if (maplen < 0) goto badfmt; | ||
1488 | + } | ||
1489 | + } else { | ||
1490 | + goto badfmt; | ||
1491 | + } | ||
1492 | + GF.rowbytes = GF.charsize / GF.height; | ||
1493 | + | ||
1494 | + return; | ||
1495 | + badfmt: | ||
1496 | + if (G.font) fb_freefont(); | ||
1497 | + bb_error_msg_and_die("Cannot read file %s - unrecognized format\n",filename); | ||
1498 | + } | ||
1499 | + #endif // FONTLOAD | ||
1500 | + #endif // TEXT | ||
1501 | |||
1502 | /** | ||
1503 | * Parse configuration file | ||
1504 | *************** | ||
1505 | *** 298,303 **** | ||
1506 | --- 1165,1173 ---- | ||
1507 | "BAR_WIDTH\0" "BAR_HEIGHT\0" | ||
1508 | "BAR_LEFT\0" "BAR_TOP\0" | ||
1509 | "BAR_R\0" "BAR_G\0" "BAR_B\0" | ||
1510 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
1511 | + "TEXT_LEFT\0" "TEXT_TOP\0" "TEXT_R\0" "TEXT_G\0" "TEXT_B\0" "TEXT_SIZE\0" | ||
1512 | + #endif | ||
1513 | #if DEBUG | ||
1514 | "DEBUG\0" | ||
1515 | #endif | ||
1516 | *************** | ||
1517 | *** 305,319 **** | ||
1518 | char *token[2]; | ||
1519 | parser_t *parser = config_open2(cfg_filename, xfopen_stdin); | ||
1520 | while (config_read(parser, token, 2, 2, "#=", | ||
1521 | ! (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) { | ||
1522 | unsigned val = xatoi_u(token[1]); | ||
1523 | int i = index_in_strings(param_names, token[0]); | ||
1524 | if (i < 0) | ||
1525 | bb_error_msg_and_die("syntax error: %s", token[0]); | ||
1526 | ! if (i >= 0 && i < 7) | ||
1527 | G.ns[i] = val; | ||
1528 | #if DEBUG | ||
1529 | ! if (i == 7) { | ||
1530 | G.bdebug_messages = val; | ||
1531 | if (G.bdebug_messages) | ||
1532 | G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log"); | ||
1533 | --- 1175,1189 ---- | ||
1534 | char *token[2]; | ||
1535 | parser_t *parser = config_open2(cfg_filename, xfopen_stdin); | ||
1536 | while (config_read(parser, token, 2, 2, "#=", | ||
1537 | ! (PARSE_NORMAL | PARSE_MIN_DIE) & ~(PARSE_TRIM | PARSE_COLLAPSE))) { | ||
1538 | unsigned val = xatoi_u(token[1]); | ||
1539 | int i = index_in_strings(param_names, token[0]); | ||
1540 | if (i < 0) | ||
1541 | bb_error_msg_and_die("syntax error: %s", token[0]); | ||
1542 | ! if (i >= 0 && i < FBCFGVALS) | ||
1543 | G.ns[i] = val; | ||
1544 | #if DEBUG | ||
1545 | ! if (i == FBCFGVALS) { | ||
1546 | G.bdebug_messages = val; | ||
1547 | if (G.bdebug_messages) | ||
1548 | G.logfile_fd = xfopen_for_write("/tmp/fbsplash.log"); | ||
1549 | *************** | ||
1550 | *** 327,337 **** | ||
1551 | int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
1552 | int fbsplash_main(int argc UNUSED_PARAM, char **argv) | ||
1553 | { | ||
1554 | const char *fb_device, *cfg_filename, *fifo_filename; | ||
1555 | ! FILE *fp = fp; // for compiler | ||
1556 | ! char *num_buf; | ||
1557 | ! unsigned num; | ||
1558 | bool bCursorOff; | ||
1559 | |||
1560 | INIT_G(); | ||
1561 | |||
1562 | --- 1197,1220 ---- | ||
1563 | int fbsplash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
1564 | int fbsplash_main(int argc UNUSED_PARAM, char **argv) | ||
1565 | { | ||
1566 | + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD | ||
1567 | + const char *fontmap_filename = NULL; | ||
1568 | + #endif | ||
1569 | const char *fb_device, *cfg_filename, *fifo_filename; | ||
1570 | ! char num_buf[512]; | ||
1571 | ! int num; | ||
1572 | bool bCursorOff; | ||
1573 | + struct timeval tim; | ||
1574 | + fd_set rdfds; | ||
1575 | + FILE *fp=fp; | ||
1576 | + int bounce_x=0; | ||
1577 | + #if ENABLE_FEATURE_FBSPLASH_SPRITES | ||
1578 | + int anim_x,anim_y, /* position of sprite or animation */ | ||
1579 | + anim_n,anim_m, /* min sprite, max sprite for animation */ | ||
1580 | + anim_p=0, /* actual sprite */ | ||
1581 | + anim_t=0,anim_now=0; /* time between steps */ | ||
1582 | + struct sprite *spa=NULL; | ||
1583 | + #endif | ||
1584 | |||
1585 | INIT_G(); | ||
1586 | |||
1587 | *************** | ||
1588 | *** 339,347 **** | ||
1589 | fb_device = "/dev/fb0"; | ||
1590 | cfg_filename = NULL; | ||
1591 | fifo_filename = NULL; | ||
1592 | bCursorOff = 1 & getopt32(argv, "cs:d:i:f:", | ||
1593 | &G.image_filename, &fb_device, &cfg_filename, &fifo_filename); | ||
1594 | ! | ||
1595 | // parse configuration file | ||
1596 | if (cfg_filename) | ||
1597 | init(cfg_filename); | ||
1598 | --- 1222,1240 ---- | ||
1599 | fb_device = "/dev/fb0"; | ||
1600 | cfg_filename = NULL; | ||
1601 | fifo_filename = NULL; | ||
1602 | + | ||
1603 | + #if ENABLE_FEATURE_FBSPLASH_FONTLOAD | ||
1604 | + bCursorOff = 1 & getopt32(argv, "cs:d:i:f:m:", | ||
1605 | + &G.image_filename, &fb_device, &cfg_filename, &fifo_filename, &fontmap_filename); | ||
1606 | + | ||
1607 | + if (fontmap_filename) fb_readpsf(fontmap_filename); | ||
1608 | + #else | ||
1609 | bCursorOff = 1 & getopt32(argv, "cs:d:i:f:", | ||
1610 | &G.image_filename, &fb_device, &cfg_filename, &fifo_filename); | ||
1611 | ! #endif | ||
1612 | ! #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
1613 | ! if (G.font==NULL) fb_readvgafont(); | ||
1614 | ! #endif | ||
1615 | // parse configuration file | ||
1616 | if (cfg_filename) | ||
1617 | init(cfg_filename); | ||
1618 | *************** | ||
1619 | *** 357,369 **** | ||
1620 | full_write(STDOUT_FILENO, "\x1b" "[?25l", 6); | ||
1621 | } | ||
1622 | |||
1623 | fb_drawimage(); | ||
1624 | |||
1625 | if (!fifo_filename) | ||
1626 | return EXIT_SUCCESS; | ||
1627 | |||
1628 | fp = xfopen_stdin(fifo_filename); | ||
1629 | ! if (fp != stdin) { | ||
1630 | // For named pipes, we want to support this: | ||
1631 | // mkfifo cmd_pipe | ||
1632 | // fbsplash -f cmd_pipe .... & | ||
1633 | --- 1250,1274 ---- | ||
1634 | full_write(STDOUT_FILENO, "\x1b" "[?25l", 6); | ||
1635 | } | ||
1636 | |||
1637 | + if (fifo_filename != NULL) { | ||
1638 | + G.spb2 = fb_newsprite(G.nbar_width, G.nbar_height, -1, -1); | ||
1639 | + } | ||
1640 | + | ||
1641 | fb_drawimage(); | ||
1642 | |||
1643 | + G.spb1 = fb_newsprite(G.nbar_width, G.nbar_height, G.nbar_posx, G.nbar_posy); | ||
1644 | + #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
1645 | + if (G.font != NULL) | ||
1646 | + G.spt = fb_newsprite(G.scr_var.xres - G.text_posx, | ||
1647 | + GF.height * G.text_size, | ||
1648 | + G.text_posx, G.text_posy); | ||
1649 | + #endif | ||
1650 | + | ||
1651 | if (!fifo_filename) | ||
1652 | return EXIT_SUCCESS; | ||
1653 | |||
1654 | fp = xfopen_stdin(fifo_filename); | ||
1655 | ! if (fp != stdin) { | ||
1656 | // For named pipes, we want to support this: | ||
1657 | // mkfifo cmd_pipe | ||
1658 | // fbsplash -f cmd_pipe .... & | ||
1659 | *************** | ||
1660 | *** 375,407 **** | ||
1661 | // when last writer closes input end. | ||
1662 | // The simplest way is to open fifo for writing too | ||
1663 | // and become an additional writer :) | ||
1664 | ! open(fifo_filename, O_WRONLY); // errors are ignored | ||
1665 | ! } | ||
1666 | |||
1667 | - fb_drawprogressbar(0); | ||
1668 | // Block on read, waiting for some input. | ||
1669 | // Use of <stdio.h> style I/O allows to correctly | ||
1670 | // handle a case when we have many buffered lines | ||
1671 | // already in the pipe | ||
1672 | ! while ((num_buf = xmalloc_fgetline(fp)) != NULL) { | ||
1673 | ! if (strncmp(num_buf, "exit", 4) == 0) { | ||
1674 | ! DEBUG_MESSAGE("exit"); | ||
1675 | ! break; | ||
1676 | ! } | ||
1677 | ! num = atoi(num_buf); | ||
1678 | ! if (isdigit(num_buf[0]) && (num <= 100)) { | ||
1679 | #if DEBUG | ||
1680 | ! char strVal[10]; | ||
1681 | ! sprintf(strVal, "%d", num); | ||
1682 | ! DEBUG_MESSAGE(strVal); | ||
1683 | #endif | ||
1684 | - fb_drawprogressbar(num); | ||
1685 | } | ||
1686 | - free(num_buf); | ||
1687 | } | ||
1688 | ! | ||
1689 | if (bCursorOff) // restore cursor | ||
1690 | full_write(STDOUT_FILENO, "\x1b" "[?25h", 6); | ||
1691 | |||
1692 | return EXIT_SUCCESS; | ||
1693 | } | ||
1694 | --- 1280,1416 ---- | ||
1695 | // when last writer closes input end. | ||
1696 | // The simplest way is to open fifo for writing too | ||
1697 | // and become an additional writer :) | ||
1698 | ! open(fifo_filename, O_WRONLY | O_NDELAY); // errors are ignored | ||
1699 | ! } | ||
1700 | |||
1701 | // Block on read, waiting for some input. | ||
1702 | // Use of <stdio.h> style I/O allows to correctly | ||
1703 | // handle a case when we have many buffered lines | ||
1704 | // already in the pipe | ||
1705 | ! // select and fgets work just in time only in nobuf mode | ||
1706 | ! setvbuf(fp, NULL, _IONBF, 0); | ||
1707 | ! tim.tv_sec = 0; | ||
1708 | ! tim.tv_usec = 100000; | ||
1709 | ! while (1) { | ||
1710 | ! FD_ZERO(&rdfds); | ||
1711 | ! FD_SET(fileno(fp),&rdfds); | ||
1712 | ! if ((num=select(fileno(fp) + 1,&rdfds,NULL,NULL,&tim))==-1) | ||
1713 | ! break; // select error | ||
1714 | ! if (num != 0 && fgets(num_buf,sizeof(num_buf) - 1,fp) != NULL) { | ||
1715 | ! num_buf[sizeof(num_buf) - 1]=0; | ||
1716 | ! num=strlen(num_buf) - 1; | ||
1717 | ! if (num >= 0 && num_buf[num]==10) num_buf[num]=0; | ||
1718 | ! if (strncmp(num_buf, "exit", 4) == 0) { | ||
1719 | ! DEBUG_MESSAGE("exit"); | ||
1720 | ! break; | ||
1721 | ! } else if (strncmp(num_buf, "bounce", 6) == 0) { | ||
1722 | ! bounce_x = 0; | ||
1723 | ! #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
1724 | ! } else if (strncmp(num_buf, "write:", 6) == 0) { | ||
1725 | ! if (G.font!=NULL) fb_drawstring(num_buf + 6); | ||
1726 | ! DEBUG_MESSAGE(num_buf); | ||
1727 | ! #endif | ||
1728 | ! #if ENABLE_FEATURE_FBSPLASH_SPRITES | ||
1729 | ! } else if (strncmp(num_buf, "load:", 5) == 0) { | ||
1730 | ! if (spa) { | ||
1731 | ! fb_putanim(anim_x,anim_y,0,spa); // hide old | ||
1732 | ! free(spa); | ||
1733 | ! spa=NULL; | ||
1734 | ! } | ||
1735 | ! if (sscanf(&(num_buf[5]),"%d:%d:", | ||
1736 | ! &anim_x,&anim_y) == 2) { | ||
1737 | ! // skip x:y | ||
1738 | ! for (num=5;num_buf[num] != ':';) num++; | ||
1739 | ! for (num++;num_buf[num] != ':';) num++; | ||
1740 | ! spa=fb_loadsprites(&(num_buf[num + 1]),anim_x,anim_y); | ||
1741 | ! anim_t = anim_now = 0; // stop animation | ||
1742 | ! } | ||
1743 | ! } else if (strncmp(num_buf, "sprite:", 7) == 0) { | ||
1744 | ! if (spa != NULL) { | ||
1745 | ! // "sprite:n" | ||
1746 | ! anim_t = anim_now = 0; // stop animation | ||
1747 | ! anim_p = atoi(&(num_buf[7])); | ||
1748 | ! if (anim_p >= 0) { | ||
1749 | ! fb_putanim(anim_x,anim_y,anim_p,spa); | ||
1750 | ! } | ||
1751 | ! } | ||
1752 | ! } else if (strncmp(num_buf, "anime:", 6) == 0) { | ||
1753 | ! if (spa != NULL) { | ||
1754 | ! // "anime:n:m:t" | ||
1755 | ! if ((sscanf(&(num_buf[6]),"%d:%d:%d", | ||
1756 | ! &anim_n,&anim_m,&anim_t) == 3) && | ||
1757 | ! (anim_n >= 0) && (anim_m >= 0)) { | ||
1758 | ! anim_p = anim_n; // first frame | ||
1759 | ! fb_putanim(anim_x,anim_y,anim_p,spa); | ||
1760 | ! anim_now = anim_t; // next sprite after time t | ||
1761 | ! } else { | ||
1762 | ! anim_now = anim_t = 0; // stop animation | ||
1763 | ! } | ||
1764 | ! } | ||
1765 | ! #endif | ||
1766 | ! } else { | ||
1767 | ! num = atoi(num_buf); | ||
1768 | ! if ((isdigit(num_buf[0]) || (num_buf[0]=='-')) && | ||
1769 | ! (num >= -100 && num <= 100)) { | ||
1770 | #if DEBUG | ||
1771 | ! char strVal[10]; | ||
1772 | ! sprintf(strVal, "%d", num); | ||
1773 | ! DEBUG_MESSAGE(strVal); | ||
1774 | ! #endif | ||
1775 | ! if (num < 0) { | ||
1776 | ! fb_drawnbar(-1 * num,100); | ||
1777 | ! } else { | ||
1778 | ! fb_drawnbar(0,num); | ||
1779 | ! } | ||
1780 | ! bounce_x = -1; // stop bounce | ||
1781 | ! } | ||
1782 | ! } | ||
1783 | ! } else { | ||
1784 | ! if (fp == stdin && feof(fp)) break; // stdin closed END | ||
1785 | ! } | ||
1786 | ! if (tim.tv_usec == 0) { // timeout - anime | ||
1787 | ! tim.tv_usec = 100000; | ||
1788 | ! if (bounce_x >= 0) { | ||
1789 | ! bounce_x += 5; | ||
1790 | ! if (bounce_x > 160) bounce_x = 0; | ||
1791 | ! if (bounce_x > 80) | ||
1792 | ! fb_drawnbar(160 - bounce_x,180 - bounce_x); | ||
1793 | ! else | ||
1794 | ! fb_drawnbar(bounce_x, bounce_x + 20); | ||
1795 | ! } | ||
1796 | ! #if ENABLE_FEATURE_FBSPLASH_SPRITES | ||
1797 | ! if (spa != NULL && anim_t != 0) { | ||
1798 | ! --anim_now; | ||
1799 | ! if (anim_now <= 0) { | ||
1800 | ! anim_now = anim_t; // wait | ||
1801 | ! if (anim_n < anim_m) { | ||
1802 | ! anim_p++; | ||
1803 | ! if (anim_p > anim_m) anim_p = anim_n; | ||
1804 | ! } else { | ||
1805 | ! anim_p--; | ||
1806 | ! if (anim_p < anim_m) anim_p = anim_n; | ||
1807 | ! } | ||
1808 | ! fb_putanim(anim_x,anim_y,anim_p,spa); | ||
1809 | ! } | ||
1810 | ! } | ||
1811 | #endif | ||
1812 | } | ||
1813 | } | ||
1814 | ! if (G.spb1 != NULL) free(G.spb1); | ||
1815 | ! G.spb1 = NULL; | ||
1816 | ! if (G.spb2 != NULL) free(G.spb2); | ||
1817 | ! G.spb2 = NULL; | ||
1818 | ! #if ENABLE_FEATURE_FBSPLASH_TEXT | ||
1819 | ! if (G.font != NULL) fb_freefont(); | ||
1820 | ! if (G.spt != NULL) free(G.spt); | ||
1821 | ! G.spt = NULL; | ||
1822 | ! #endif | ||
1823 | ! #if ENABLE_FEATURE_FBSPLASH_SPRITES | ||
1824 | ! if (spa != NULL) free(spa); | ||
1825 | ! #endif | ||
1826 | if (bCursorOff) // restore cursor | ||
1827 | full_write(STDOUT_FILENO, "\x1b" "[?25h", 6); | ||
1828 | |||
1829 | return EXIT_SUCCESS; | ||
1830 | } | ||
1831 | + #endif | ||
1832 | *** busybox-1.15.0/miscutils/fbsplash.cfg.orig 2009-08-21 00:26:14.000000000 +0200 | ||
1833 | --- busybox-1.15.0/miscutils/fbsplash.cfg 2009-09-01 08:42:18.000000000 +0200 | ||
1834 | *************** | ||
1835 | *** 7,9 **** | ||
1836 | --- 7,19 ---- | ||
1837 | BAR_R=80 | ||
1838 | BAR_G=80 | ||
1839 | BAR_B=130 | ||
1840 | + # the below settings are active only if you enable the option FBSPLASH_TEXT_RENDERING | ||
1841 | + # text position | ||
1842 | + #TEXT_LEFT=100 | ||
1843 | + #TEXT_TOP=350 | ||
1844 | + ## text color | ||
1845 | + #TEXT_R=80 | ||
1846 | + #TEXT_G=80 | ||
1847 | + #TEXT_B=130 | ||
1848 | + ## text size (1 to 4) | ||
1849 | + #TEXT_SIZE=2 |