zebra.c 15.7 KB
Newer Older
1
2
3
4
/** \file
 * Zebra stripes, contrast edge detection and crop marks.
 *
 */
5
6
/*
 * Copyright (C) 2009 Trammell Hudson <hudson+ml@osresearch.net>
7
 * Edge detection code by Robert Thiel <rthiel@gmail.com>
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301, USA.
 */

26
27
#include "dryos.h"
#include "bmp.h"
28
#include "version.h"
29
#include "config.h"
30
#include "menu.h"
31
#include "property.h"
32

33

34
static struct bmp_file_t * cropmarks;
35
36
static volatile unsigned lv_drawn = 0;
static volatile unsigned sensor_cleaning = 1;
hudson@kremvax's avatar
hudson@kremvax committed
37

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
38
39
40
#define vram_start_line	33
#define vram_end_line	380

41
42
43
44
#define hist_height			64
#define hist_width			128
#define waveform_height			256
#define waveform_width			(720/2)
45

46
47
48
49
50
51
CONFIG_INT( "zebra.draw",	zebra_draw,	1 );
CONFIG_INT( "zebra.level",	zebra_level,	0xF000 );
CONFIG_INT( "crop.draw",	crop_draw,	1 );
CONFIG_STR( "crop.file",	crop_file,	"A:/cropmarks.bmp" );
CONFIG_INT( "edge.draw",	edge_draw,	0 );
CONFIG_INT( "enable-liveview",	enable_liveview, 1 );
52
CONFIG_INT( "hist.draw",	hist_draw,	1 );
53
CONFIG_INT( "hist.x",		hist_x,		720 - hist_width );
54
CONFIG_INT( "hist.y",		hist_y,		100 );
55
CONFIG_INT( "waveform.draw",	waveform_draw,	0 );
56
57
58
CONFIG_INT( "waveform.x",	waveform_x,	720 - waveform_width );
CONFIG_INT( "waveform.y",	waveform_y,	480 - 50 - waveform_height );
CONFIG_INT( "waveform.bg",	waveform_bg,	0x26 ); // solid black
59
CONFIG_INT( "timecode.x",	timecode_x,	720 - 160 );
hudson@kremvax's avatar
hudson@kremvax committed
60
CONFIG_INT( "timecode.y",	timecode_y,	32 );
61
62
CONFIG_INT( "timecode.width",	timecode_width,	160 );
CONFIG_INT( "timecode.height",	timecode_height, 20 );
63
CONFIG_INT( "timecode.warning",	timecode_warning, 120 );
64
static unsigned timecode_font	= FONT(FONT_MED, COLOR_RED, COLOR_BG );
65

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
66

67
/** Sobel edge detection */
68
static int32_t
69
edge_detect(
70
	uint32_t *		buf,
71
72
	uint32_t		pitch
)
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
{	
	const uint32_t		pixel1	= buf[0];
	const int32_t		p00	= (pixel1 & 0xFFFF);
	const int32_t		p01	= pixel1 >> 16;
	const uint32_t		pixel2	= buf[1];
	const int32_t		p02	= (pixel2 & 0xFFFF);
	const uint32_t		pixel3	= buf[pitch];
	const int32_t		p10	= (pixel3 & 0xFFFF);
	const int32_t		p11	= pixel3 >> 16;
	const uint32_t		pixel4	= buf[pitch+1];
	const int32_t		p12	= (pixel4 & 0xFFFF);
	
	int32_t sx1 = p00 - p11;
	int32_t sy1 = p01 - p10;
	
	int32_t sx2 = p01 - p12;
	int32_t sy2 = p02 - p11;
90

91
	// abs value
92
93
94
95
96
97
98
	sx1 = ( sx1 ^ (sx1 >> 15) ) - (sx1 >> 15);
	sy1 = ( sy1 ^ (sy1 >> 15) ) - (sy1 >> 15);
	
	sx2 = ( sx2 ^ (sx2 >> 15) ) - (sx2 >> 15);
	sy2 = ( sy2 ^ (sy2 >> 15) ) - (sy2 >> 15);
	
	return (((sx2 + sy2) >> 1 ) & 0xFF00 ) | ((sx1 + sy1) >> 9);
99
100
101
}


102
103
104
105
106
107
108
109
110
static unsigned
check_edge(
	unsigned		x,
	unsigned		y,
	uint16_t *		b_row,
	uint32_t *		v_row,
	unsigned		vram_pitch
)
{
111
	const unsigned dx = x/2;
112
	// Check for contrast
113
114
	uint32_t grad = edge_detect(
		&v_row[dx],
115
116
		vram_pitch
	);
117
118
119
	
	// Check for any high gradients in either pixel
	if( (grad & 0xF8F8) == 0 )
120
121
		return 0;

122
	// Color coding (using the blue colors starting at 0x70)
123
	b_row[dx] = 0x7070 | ((grad & 0xF8F8) >> 3) ;			
124
	return 1;
125
	
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
}


static unsigned
check_zebra(
	unsigned		x,
	unsigned		y,
	uint16_t *		b_row,
	uint32_t *		v_row,
	unsigned		vram_pitch
)
{
	const uint8_t zebra_color_0 = COLOR_BG; // 0x6F; // bright read
	const uint8_t zebra_color_1 = 0x5F; // dark red

	uint32_t pixel = v_row[x/2];
142
143
	uint32_t p0 = (pixel >> 16) & 0xFFFF;
	uint32_t p1 = (pixel >>  0) & 0xFFFF;
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185

	// If neither pixel is overexposed, ignore it
	if( p0 < zebra_level && p1 < zebra_level )
		return 0;

	// Determine if we are a zig or a zag line
	uint32_t zag = ((y >> 3) ^ (x >> 3)) & 1;

	// Build the 16-bit word to write both pixels
	// simultaneously into the BMP VRAM
	uint16_t zebra_color_word = zag
		? (zebra_color_0<<8) | (zebra_color_0<<0)
		: (zebra_color_1<<8) | (zebra_color_1<<0);

	b_row[x/2] = zebra_color_word;
	return 1;
}


static unsigned
check_crop(
	unsigned		x,
	unsigned		y,
	uint16_t *		b_row,
	uint32_t *		v_row,
	unsigned		vram_pitch
)
{
	if( !cropmarks )
		return 0;

	uint8_t * pixbuf = &cropmarks->image[
		x + cropmarks->width * (cropmarks->height - y)
	];
	uint16_t pix = *(uint16_t*) pixbuf;
	if( pix == 0 )
		return 0;

	b_row[ x/2 ] = pix;
	return 1;
}

186

187
188
189
190
/** Store the waveform data for each of the waveform_width bins with
 * 128 levels
 */
static uint32_t waveform[ waveform_width ][ waveform_height ];
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
191

192
/** Store the histogram data for each of the 128 bins */
193
static uint32_t hist[ hist_width ];
194
195
196

/** Maximum value in the histogram so that at least one entry fills
 * the box */
197
198
199
static uint32_t hist_max;


200
201
202
203
204
205
206
207
208
209
210
/** Generate the histogram data from the YUV frame buffer.
 *
 * Walk the frame buffer two pixels at a time, in 32-bit chunks,
 * to avoid err70 while recording.
 *
 * Average two adjacent pixels to try to reduce noise slightly.
 *
 * Update the hist_max for the largest number of bin entries found
 * to scale the histogram to fit the display box from top to
 * bottom.
 */
211
212
213
214
static void
hist_build( void )
{
	struct vram_info *	vram = &vram_info[ vram_get_number(2) ];
215
	const uint32_t * 	v_row = (uint32_t*) vram->vram;
216
217
218
219
220
	const unsigned		width = vram->width;
	uint32_t x,y;

	hist_max = 0;

221
#if 0
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
222
223
	memset( hist, 0, sizeof(hist) );
	memset( waveform, 0, sizeof(waveform) );
224
225
226
227
228
229
230
231
232
233
234
#else
	// memset() causes err70?  Too much memory bandwidth?
	for( x=0 ; x<hist_width ; x++ )
		hist[x] = 0;
	for( y=0 ; y<waveform_width ; y++ )
		for( x=0 ; x<waveform_height ; x++ )
		{
			waveform[y][x] = 0;
			asm( "nop\nnop\nnop\nnop\n" );
		}
#endif
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
235
236

	for( y=vram_start_line ; y<vram_end_line; y++, v_row += (vram->pitch/2) )
237
238
239
	{
		for( x=0 ; x<width ; x += 2 )
		{
240
			// Average each of the two pixels
241
			uint32_t pixel = v_row[x/2];
242
243
244
245
246
			uint32_t p1 = (pixel >> 16) & 0xFFFF;
			uint32_t p2 = (pixel >>  0) & 0xFFFF;
			uint32_t p = (p1+p2) / 2;

			uint32_t hist_level = ( p * hist_width ) / 65536;
247

248
			// Ignore the 0 bin.  It generates too much noise
249
250
			unsigned count = ++hist[ hist_level ];
			if( hist_level && count > hist_max )
251
				hist_max = count;
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
252
253

			// Update the waveform plot
254
			waveform[ (x * waveform_width) / width ][ (p * waveform_height) / 65536 ]++;
255
256
257
258
259
		}
	}
}
	

260
261
262
263
264
265
/** Draw the histogram image into the bitmap framebuffer.
 *
 * Draw one pixel at a time; it seems to be ok with err70.
 * Since there is plenty of math per pixel this doesn't
 * swamp the bitmap framebuffer hardware.
 */
266
267
268
269
270
271
272
static void
hist_draw_image(
	unsigned		x_origin,
	unsigned		y_origin
)
{
	uint8_t * const bvram = bmp_vram();
273
274
275
276

	// Align the x origin, just in case
	x_origin &= ~3;

277
278
279
280
281
	uint8_t * row = bvram + x_origin + y_origin * bmp_pitch();
	if( hist_max == 0 )
		hist_max = 1;

	unsigned i, y;
282
283

	for( i=0 ; i<hist_width ; i++ )
284
	{
285
286
		// Scale by the maximum bin value
		const uint32_t size = (hist[i] * hist_height) / hist_max;
287
288
289
		uint8_t * col = row + i;

		// vertical line up to the hist size
290
		for( y=hist_height ; y>0 ; y-- , col += bmp_pitch() )
291
292
293
294
295
296
297
298
			*col = y > size ? COLOR_BG : COLOR_WHITE;
	}

	if(0) bmp_printf(
		FONT(FONT_SMALL,COLOR_RED,COLOR_WHITE),
		x_origin,
		y_origin,
		"max %d",
299
		(int) hist_max
300
301
302
303
304
305
	);

	hist_max = 0;
}


hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
306
307
308
309
310
311
312
313
314
315
316
317
/** Draw the waveform image into the bitmap framebuffer.
 *
 * Draw one pixel at a time; it seems to be ok with err70.
 * Since there is plenty of math per pixel this doesn't
 * swamp the bitmap framebuffer hardware.
 */
static void
waveform_draw_image(
	unsigned		x_origin,
	unsigned		y_origin
)
{
318
319
320
	// Ensure that x_origin is quad-word aligned
	x_origin &= ~3;

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
321
	uint8_t * const bvram = bmp_vram();
322
323
	unsigned pitch = bmp_pitch();
	uint8_t * row = bvram + x_origin + y_origin * pitch;
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
324
325
326
327
328
	if( hist_max == 0 )
		hist_max = 1;

	unsigned i, y;

329
330
	// vertical line up to the hist size
	for( y=waveform_height-1 ; y>0 ; y-- )
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
331
	{
332
		uint32_t pixel = 0;
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
333

334
		for( i=0 ; i<waveform_width ; i++ )
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
335
		{
336

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
337
338
339
340
341
342
			uint32_t count = waveform[ i ][ y ];
			// Scale to a grayscale
			count = (count * 42) / 128;
			if( count > 42 )
				count = 0x0F;
			else
343
			if( count >  0 )
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
344
				count += 0x26;
345
346
347
348
349
350
351
352
353
354
355
356
357
			else
			// Draw a series of colored scales
			if( y == (waveform_height*1)/4 )
				count = COLOR_BLUE;
			else
			if( y == (waveform_height*2)/4 )
				count = 0xE; // pink
			else
			if( y == (waveform_height*3)/4 )
				count = COLOR_BLUE;
			else
				count = waveform_bg; // transparent

358
359
360
361
362
363
			pixel <<= 8;
			pixel |= count;

			if( (i & 3) != 3 )
				continue;

364
365
366
			// Draw the pixel, rounding down to the nearest
			// quad word write (and then nop to avoid err70).
			*(uint32_t*)( row + (i & ~3)  ) = pixel;
367
368
369
370
371
			pixel = 0;
			asm( "nop" );
			asm( "nop" );
			asm( "nop" );
			asm( "nop" );
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
372
		}
373
374

		row += pitch;
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
375
376
377
378
	}
}


379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/** Master video overlay drawing code.
 *
 * This routine controls the display of the zebras, histogram,
 * edge detection, cropmarks and so on.
 *
 * The stacking order of the overlays is:
 *
 * - Histogram
 * - Cropping bitmap
 * - Zebras
 * - Edge detection
 *
 * This should be done with a proper OO controller that allows modules
 * to register new drawing functions, but for right now they are hardcoded.
 */
394
395
396
static void
draw_zebra( void )
{
397
	uint8_t * const bvram = bmp_vram();
398
399

	// If we don't have a bitmap vram yet, nothing to do.
400
	if( !bvram )
401
402
		return;

403
	// If we are not drawing edges, or zebras or crops, nothing to do
404
	if( !edge_draw && !zebra_draw && !hist_draw && !waveform_draw )
405
406
407
408
409
410
411
	{
		if( !crop_draw )
			return;
		if( !cropmarks )
			return;
	}

412
413
	struct vram_info * vram = &vram_info[ vram_get_number(2) ];

414
415
	hist_build();

416
417
	// skip the audio meter at the top and the bar at the bottom
	// hardcoded; should use a constant based on the type of display
418
	// 33 is the bottom of the meters; 55 is the crop mark
419
	uint32_t x,y;
420
	for( y=33 ; y < 390; y++ )
421
422
	{
		uint32_t * const v_row = (uint32_t*)( vram->vram + y * vram->pitch );
423
		uint16_t * const b_row = (uint16_t*)( bvram + y * bmp_pitch() );
424

425
426
427
428
		// Iterate over the pixels in the scan row
		// two at a time to read the pixel buf in 32 bit chunks
		// otherwise we get err70 aborts while drawing regions
		// in the bitmap vram.
429
		for( x=2 ; x < vram->width-2 ; x+=2 )
430
		{
431
432
433
434
			// Abort as soon as the new menu is drawn
			if( gui_menu_task || !lv_drawn )
				return;

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
435
			// Ignore the regions where the histogram will be drawn
436
			if( hist_draw
437
438
439
440
			&&  y >= hist_y
			&&  y <  hist_y + hist_height
			&&  x >= hist_x
			&&  x <  hist_x + hist_width
441
			)
442
443
				continue;

hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
444
			// Ignore the regions where the waveform will be drawn
445
			if( waveform_draw
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
446
447
448
449
450
451
452
			&&  y >= waveform_y
			&&  y <  waveform_y + waveform_height
			&&  x >= waveform_x
			&&  x <  waveform_x + waveform_width
			)
				continue;

453
454
455
456
457
458
459
			// Ignore the timecode region
			if( y >= timecode_y
			&&  y <  timecode_y + timecode_height
			&&  x >= timecode_x
			&&  x <  timecode_x + timecode_width
			)
				continue;
460

461
			if( crop_draw && check_crop( x, y, b_row, v_row, vram->pitch ) )
462
463
				continue;

464
465
			if( edge_draw && check_edge( x, y, b_row, v_row, vram->pitch ) )
				continue;
466

467
468
			if( zebra_draw && check_zebra( x, y, b_row, v_row, vram->pitch ) )
				continue;
469

470
471
			// Nobody drew on it, make it clear
			b_row[x/2] = 0;
472
473
		}
	}
474
475

	if( hist_draw )
476
		hist_draw_image( hist_x, hist_y );
477
	if( waveform_draw )
hudson@kremvax.wan's avatar
hudson@kremvax.wan committed
478
		waveform_draw_image( waveform_x, waveform_y );
479
480
481
}


482
483
static void
zebra_toggle( void * priv )
484
485
486
487
488
{
	unsigned * ptr = priv;
	*ptr = (*ptr + 0x4000) & 0xF000;
}

489
490
491

static void
zebra_display( void * priv, int x, int y, int selected )
492
{
493
494
495
	bmp_printf( 
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
496
497
		//23456789012
		"Zebra thrs: 0x%04x",
498
499
500
501
		*(unsigned*) priv
	);
}

502
503
static void
zebra_draw_display( void * priv, int x, int y, int selected )
504
{
505
506
507
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
508
509
510
		//23456789012
		"Zebras:     %s",
		*(unsigned*) priv ? "ON " : "OFF"
511
512
513
	);
}

514
515
static void
crop_display( void * priv, int x, int y, int selected )
516
{
517
518
	extern int retry_count;

519
520
521
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
522
		//23456789012
523
524
525
		"Cropmarks:  %s %d",
		cropmarks ? (*(unsigned*) priv ? "ON " : "OFF") : "NO FILE",
		retry_count
526
527
	);
}
528

529

530
531
532
533
534
535
536
537
538
539
540
541
542
static void
edge_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		//23456789012
		"Edgedetect: %s",
		*(unsigned*) priv ? "ON " : "OFF"
	);
}


543
544
545
546
547
548
549
550
551
552
553
554
static void
hist_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		//23456789012
		"Histogram:  %s",
		*(unsigned*) priv ? "ON " : "OFF"
	);
}

555
556
557
558
559
560
561
562
563
564
565
566
567
static void
waveform_display( void * priv, int x, int y, int selected )
{
	bmp_printf(
		selected ? MENU_FONT_SEL : MENU_FONT,
		x, y,
		//23456789012
		"Waveform:   %s",
		*(unsigned*) priv ? "ON " : "OFF"
	);
}


568
569
570
struct menu_entry zebra_menus[] = {
	{
		.priv		= &zebra_draw,
571
		.select		= menu_binary_toggle,
572
573
574
575
576
577
578
579
580
		.display	= zebra_draw_display,
	},
	{
		.priv		= &zebra_level,
		.select		= zebra_toggle,
		.display	= zebra_display,
	},
	{
		.priv		= &crop_draw,
581
		.select		= menu_binary_toggle,
582
583
		.display	= crop_display,
	},
584
585
586
587
588
	{
		.priv		= &edge_draw,
		.select		= menu_binary_toggle,
		.display	= edge_display,
	},
589
590
591
592
593
	{
		.priv		= &hist_draw,
		.select		= menu_binary_toggle,
		.display	= hist_display,
	},
594
595
596
597
598
	{
		.priv		= &waveform_draw,
		.select		= menu_binary_toggle,
		.display	= waveform_display,
	},
599
};
600

601
static void * lv_token;
602

603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
static void
lv_token_handler(
	void *			token
)
{
	lv_token = token;
}


static void
lv_prop_handler(
	unsigned		property,
	void *			priv,
	unsigned *		addr,
	unsigned		len
)
{
620
621
	unsigned value = *addr;

622
623
624
	switch( property )
	{
	case PROP_LV_ACTION:
625
626
		// LV_START==0, LV_STOP=1
		lv_drawn = !value;
627
628
		break;
	case PROP_GUI_STATE:
629
630
631
632
633
634
		// PLAYMENU==0, IDLE==1
		lv_drawn = !value;
		break;
	case PROP_ACTIVE_SWEEP_STATUS:
		// Let us know when the sensor is done cleaning
		sensor_cleaning = value;
635
		break;
636
637
638
639
640
641
642
643
644
645
646
647
	case PROP_MVR_REC_START:
		if( value == 2 )
			bmp_printf(
				timecode_font,
				timecode_x,
				timecode_y,
				"REC: "
			);
		break;
	case PROP_REC_TIME:
		value /= 200; // why? it seems to work out
		bmp_printf(
648
			value < timecode_warning ? timecode_font : FONT_MED,
649
650
651
652
653
654
655
			timecode_x + 5 * fontspec_font(timecode_font)->width,
			timecode_y,
			"%4d:%02d",
			value / 60,
			value % 60
		);
		break;
656
657
658
659
660
661
662
	default:
		break;
	}

	prop_cleanup( lv_token, property );
}

663
664

static void
665
666
zebra_task( void )
{
667
668
669
	static unsigned properties[] = {
		PROP_LV_ACTION,
		PROP_GUI_STATE,
670
		PROP_ACTIVE_SWEEP_STATUS,
671
672
		PROP_MVR_REC_START,
		PROP_REC_TIME,
673
674
675
676
677
678
679
680
681
682
	};

	prop_register_slave(
		properties,
		COUNT(properties),
		lv_prop_handler,
		0,
		lv_token_handler
	);

683
	lv_drawn = 0;
684
	cropmarks = bmp_load( crop_file );
685
686
687
688

	DebugMsg( DM_MAGIC, 3,
		"%s: Zebras=%s threshold=%x cropmarks=%x liveview=%d",
		__func__,
689
		zebra_draw ? "ON " : "OFF",
690
691
692
693
694
		zebra_level,
		(unsigned) cropmarks,
		enable_liveview
	);

695
696
697
698
699
700
701
702
703
704
	if( cropmarks )
	{
		DebugMsg( DM_MAGIC, 3,
			"Cropmarks: %dx%d @ %d",
			cropmarks->width,
			cropmarks->height,
			cropmarks->bits_per_pixel
		);
	}

705
706
707
708
709
710
711
712
713
	if( enable_liveview )
	{
/*
		DebugMsg( DM_MAGIC, 3, "Waiting for sweep status to end" );
		while( sensor_cleaning )
			msleep(100);
*/
		DebugMsg( DM_MAGIC, 3, "Entering liveview" );
		call( "FA_StartLiveView" );
714
715
716

		// Clear the bitmap display, just in case
		bmp_fill( 0, 0, 0, 720, 480 );
717
718
719
720
721
	}


	menu_add( "Video", zebra_menus, COUNT(zebra_menus) );

722
723
	while(1)
	{
724
		if( !gui_menu_task && lv_drawn )
725
726
727
728
		{
			draw_zebra();
			msleep( 100 );
		} else {
729
730
			// Don't display the zebras over the menu.
			// wait a while and then try again
731
732
			msleep( 500 );
		}
733
734
	}
}
735
736
737


TASK_CREATE( "zebra_task", zebra_task, 0, 0x1f, 0x1000 );