Adding Boot Screen and Interpolation Algorithm in Boot Loader
Recently a friend asked me to add a boot screen to his board. It uses the ubiquitous Samsung 2410 processor. The difference is his board has 640x480 VGA output instead of the usual 240x320 LCD.
In Windows CE, the usual method for adding a boot screen is crude — since the OS hasn’t started, there’s no BitBlt, StretchBlt, etc. The only way is to write directly to the video card’s framebuffer during startup. This is typically done in the Boot Loader. If done during OS startup, it’s usually too late — without an artificial delay, the boot screen appears briefly before the OS screen takes over. The approach is:
BOOL OEMPlatformInit() { // Init the Display InitDisplay(); }
Then InitDisplay typically looks like:
static void InitDisplay() { int i = 0; int j = 0; volatile IOPreg *s2410IOP; volatile LCDreg *s2410LCD;
s2410IOP = (IOPreg *)IOP_BASE;
s2410LCD = (LCDreg *)LCD_BASE;
// LCD port initialize.
s2410IOP->rGPCUP = 0xFFFFFFFF;
s2410IOP->rGPCCON = 0xAAAAAAAA;
s2410IOP->rGPCCON = 0xAAAAAAAA;
s2410IOP->rGPDUP = 0xFFFFFFFF;
s2410IOP->rGPDCON = 0xAAAAAAAA;
s2410IOP->rGPGCON &= ~(3 << 8);
s2410IOP->rGPGCON |= (1 << 8);
s2410IOP->rGPGDAT |= (1 << 4);
s2410LCD->rLCDCON1=(1<<8)|(MVAL_USED<<7)|(3<<5)|(12<<1)|0;
s2410LCD->rLCDCON2=(VBPD<<24)|(LINEVAL_TFT<<14)|(VFPD<<6)|(VSPW);
s2410LCD->rLCDCON3=(HBPD<<19)|(HOZVAL_TFT<<8)|(HFPD);
s2410LCD->rLCDCON4=(MVAL<<8)|(HSPW);
s2410LCD->rLCDCON5=(1<<11)|(1<<9)|(1<<8)|(1<<3)|(1<<0);
s2410LCD->rLCDSADDR1=((FRAMEBUF_DMA_BASE>>22)<<21)|M5D(FRAMEBUF_DMA_BASE>>1);
s2410LCD->rLCDSADDR2=M5D((FRAMEBUF_DMA_BASE+(LCD_XSIZE_TFT*LCD_YSIZE_TFT*2))>>1);
s2410LCD->rLCDSADDR3=(((LCD_XSIZE_TFT-LCD_XSIZE_TFT)/1)<<11)|(LCD_XSIZE_TFT/1);
s2410LCD->rLPCSEL&=(~7);
s2410LCD->rTPAL=0;
s2410LCD->rLCDCON1 |= 1;
// Display a bitmap image on the LCD
//memcpy((void *)FRAMEBUF_BASE, ScreenBitmap, ARRAY_SIZE_TFT_16BIT);
// Jason : Interpolation
EdbgOutputDebugString("+Interpolation\r\n");
for(i = 0; i < 320 * 240; i++)
{
PWORD pWord = (PWORD)ScreenBitmap;
PWORD pFrmBuf = (PWORD)FRAMEBUF_BASE;
pFrmBuf[4 * i - 2 * (i % 320)] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 1] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 320 * 2] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 1 + 320 * 2] = pWord[i];
} }
The hardware initialization code is nothing special. Note the memcpy line — this is the key to displaying the boot screen, directly copying a large array to FRAMEBUF_BASE. This fills the video card buffer. The large array can be generated by Image2LCD or similar tools that convert a bitmap to a C array — commonly used in game development.
Normally this method works fine. But the problem: the target device has 640x480x16-bit display, so the array needs 640 * 480 * 2 = 600KB. For a typical boot loader, only 256KB is allocated (configurable, but this board uses Linux’s loader to boot CE’s loader, causing complications). Two solutions:
- Compress, then decompress at runtime, then memcpy. Use some basic open-source compression algorithm.
- Interpolation: use a small image and interpolate it to 640x480.
I chose interpolation — less work. Basically implementing StretchBlt myself.
I didn’t take graphics courses in undergrad, so I don’t know if there’s a formula for interpolation. I derived it myself. Since this is just a 4x放大 special case (one pixel becomes four), it’s straightforward. After some scribbling, I got the formula. Hard to post formulas, here’s the code:
for(i = 0; i < 320 * 240; i++) { PWORD pWord = (PWORD)ScreenBitmap; PWORD pFrmBuf = (PWORD)FRAMEBUF_BASE;
pFrmBuf[4 * i - 2 * (i % 320)] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 1] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 320 * 2] = pWord[i];
pFrmBuf[4 * i - 2 * (i % 320) + 1 + 320 * 2] = pWord[i]; }
This time I prepared a 320x240 image and used a single loop to write it to the framebuffer.
The result was pretty good. I wonder if there’s a better interpolation formula. If so, please tell me.