/** \file * Drawing routines. * * These are Magic Lantern routines to draw into the BMP LVRAM. * They are not derived from DryOS routines. */ /* * Copyright (C) 2009 Trammell Hudson * * 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. */ #include "dryos.h" #include "bmp.h" #include "font.h" #include #include "propvalues.h" //~ int bmp_enabled = 1; static int bmp_idle_flag = 0; void bmp_draw_to_idle(int value) { bmp_idle_flag = value; } // 0 = copy BMP to idle // 1 = copy idle to BMP void bmp_idle_copy(int direction, int fullsize) { uint8_t* real = bmp_vram_real(); uint8_t* idle = bmp_vram_idle(); ASSERT(real) ASSERT(idle) if (fullsize) { if (direction) memcpy(BMP_VRAM_START(real), BMP_VRAM_START(idle), BMP_VRAM_SIZE); else memcpy(BMP_VRAM_START(idle), BMP_VRAM_START(real), BMP_VRAM_SIZE); } else { #ifdef CONFIG_VXWORKS if (direction) { for (int i = 0; i < 240; i ++) memcpy(real+i*BMPPITCH, idle+i*BMPPITCH, 360); } else { for (int i = 0; i < 240; i ++) memcpy(idle+i*BMPPITCH, real+i*BMPPITCH, 360); } #else if (direction) { for (int i = 0; i < 480; i ++) memcpy(real+i*BMPPITCH, idle+i*BMPPITCH, 720); } else { for (int i = 0; i < 480; i ++) memcpy(idle+i*BMPPITCH, real+i*BMPPITCH, 720); } #endif } } /* void bmp_idle_clear() { bzero32(BMP_VRAM_START(bmp_vram_idle()), BMP_VRAM_SIZE); }*/ /** Returns a pointer to currently selected BMP vram (real or mirror) */ uint8_t * bmp_vram(void) { #ifdef CONFIG_VXWORKS set_ml_palette_if_dirty(); #endif uint8_t * bmp_buf = bmp_idle_flag ? bmp_vram_idle() : bmp_vram_real(); return bmp_buf; } #define USE_LUT static void _draw_char( unsigned fontspec, uint8_t * bmp_vram_row, char c ) { uint8_t* v = bmp_vram(); if (bmp_vram_row < v) return; unsigned i,j; const struct font * const font = fontspec_font( fontspec ); if (bmp_vram_row >= BMP_VRAM_END(v)) return; uint32_t fg_color = fontspec_fg( fontspec ) << 24; uint32_t bg_color = fontspec_bg( fontspec ) << 24; // Special case -- fg=bg=0 => white on black if( fg_color == 0 && bg_color == 0 ) { fg_color = COLOR_WHITE << 24; bg_color = COLOR_BLACK << 24; } const uint32_t pitch = BMPPITCH / 4; uint32_t * front_row = (uint32_t *) bmp_vram_row; // boundary checking, don't write past this address uint32_t* end = (uint32_t *)(BMP_VRAM_END(v) - font->width); #ifndef CONFIG_VXWORKS //uint32_t flags = cli(); if ((fontspec & SHADOW_MASK) == 0) { for( i=0 ; iheight ; i++ ) { // Start this scanline uint32_t * row = front_row; if (row >= end) return; // move to the next scanline front_row += pitch; uint32_t pixels = font->bitmap[ c + (i << 7) ]; uint8_t pixel; for( j=0 ; jwidth/4 ; j++ ) { uint32_t bmp_pixels = 0; for( pixel=0 ; pixel<4 ; pixel++, pixels <<=1 ) { bmp_pixels >>= 8; bmp_pixels |= (pixels & 0x80000000) ? fg_color : bg_color; } *(row++) = bmp_pixels; // handle characters wider than 32 bits //~ if( j == 28/4 ) //~ pixels = font->bitmap[ c + ((i+128) << 7) ]; } } } else // shadowed fonts { struct font * shadow = #ifdef CONFIG_STATIC_FONTS (struct font *) font; #else font == &font_large ? &font_large_shadow : font == &font_med ? &font_med_shadow : font == &font_small ? &font_small_shadow : 0; #endif fg_color >>= 24; bg_color >>= 24; for( i=0 ; iheight ; i++ ) { // Start this scanline uint32_t * row = front_row; row = (uint32_t*)((unsigned)row & ~3); // weird artifacts otherwise if (row >= end) return; // move to the next scanline front_row += pitch; uint32_t pixels = font->bitmap[ c + (i << 7) ]; uint32_t pixels_shadow = shadow->bitmap[ c + (i << 7) ]; uint8_t pixel; for( j=0 ; jwidth/4 ; j++ ) { uint32_t bmp_pixels = *(row); for( pixel=0 ; pixel<4 ; pixel++, pixels <<=1, pixels_shadow <<=1 ) { //~ bmp_pixels >>= 8; //~ bmp_pixels |= (*(row) & 0xFF000000); if (pixels & 0x80000000) { bmp_pixels &= ~(0xFF << (pixel*8)); bmp_pixels |= (fg_color << (pixel*8)); } if (pixels_shadow & 0x80000000) { bmp_pixels &= ~(0xFF << (pixel*8)); bmp_pixels |= (bg_color << (pixel*8)); } } *(row++) = bmp_pixels; } } } #else // 5DC #define FPIX(i,j) (font->bitmap[ c + ((i) << 7) ] & (1 << (31-(j)))) //- #define BMPIX(i,j) bmp_vram_row[(i) * BMPPITCH + (j)] #define BMPIX(i,j,color) char* p = &bmp_vram_row[((i)/2) * BMPPITCH + (j)/2]; SET_4BIT_PIXEL(p, j, color); if (font == &font_large) // large fonts look better with line skipping { for( i = 0 ; iheight ; i++ ) { for( j=0 ; jwidth ; j++ ) { if FPIX(i,j) { BMPIX(i,j,fg_color>>24); } else { BMPIX(i,j,bg_color>>24); } } } } else // smaller fonts look better with all foreground pixels displayed (even if they are a bit bolder than normal) { for( i = 0 ; iheight ; i++ ) { for( j=0 ; jwidth ; j++ ) { if (!FPIX(i,j)) { BMPIX(i,j,bg_color>>24); } } } for( i = 0 ; iheight ; i++ ) { for( j=0 ; jwidth ; j++ ) { if FPIX(i,j) { BMPIX(i,j,fg_color>>24); } } } } #endif } void bmp_puts( unsigned fontspec, unsigned * x, unsigned * y, const char * s ) { ASSERT(x) ASSERT(y) ASSERT(s) const uint32_t pitch = BMPPITCH; uint8_t * vram = bmp_vram(); if( !vram || ((uintptr_t)vram & 1) == 1 ) return; const unsigned initial_x = *x; #ifdef CONFIG_VXWORKS uint8_t * first_row = vram + ((*y)/2) * pitch + ((*x)/2); #else uint8_t * first_row = vram + (*y) * pitch + (*x); #endif uint8_t * row = first_row; char c; const struct font * const font = fontspec_font( fontspec ); while( (c = *s++) ) { if( c == '\n' ) { #ifdef CONFIG_VXWORKS row = first_row += pitch * font->height/2; #else row = first_row += pitch * font->height; #endif (*y) += font->height; (*x) = initial_x; continue; } if( c == '\b' ) { (*x) -= font->width; continue; } _draw_char( fontspec, row, c ); #ifdef CONFIG_VXWORKS row += font->width / 2; #else row += font->width; #endif (*x) += font->width; } } void bmp_puts_w( unsigned fontspec, int * x, int * y, int max_chars_per_line, const char * s ) { ASSERT(x) ASSERT(y) ASSERT(s) const uint32_t pitch = BMPPITCH; uint8_t * vram = bmp_vram(); if( !vram || ((uintptr_t)vram & 1) == 1 ) return; const int initial_x = *x; uint8_t * first_row = vram + (*y) * pitch + (*x); uint8_t * row = first_row; char c; const struct font * const font = fontspec_font( fontspec ); int i = 0; while( (c = *s++) ) { if( c == '\n' || i >= max_chars_per_line) { row = first_row += pitch * font->height; (*y) += font->height; (*x) = initial_x; i = 0; if (lv) msleep(1); if (c == '\n') continue; } _draw_char( fontspec, row, c ); row += font->width; (*x) += font->width; i++; } } // thread safe void bmp_printf( unsigned fontspec, unsigned x, unsigned y, const char * fmt, ... ) { va_list ap; char bmp_printf_buf[128]; va_start( ap, fmt ); vsnprintf( bmp_printf_buf, sizeof(bmp_printf_buf), fmt, ap ); va_end( ap ); bmp_puts( fontspec, &x, &y, bmp_printf_buf ); } // for very large strings only void big_bmp_printf( unsigned fontspec, unsigned x, unsigned y, const char * fmt, ... ) { BMP_LOCK( va_list ap; static char bmp_printf_buf[1024]; va_start( ap, fmt ); vsnprintf( bmp_printf_buf, sizeof(bmp_printf_buf), fmt, ap ); va_end( ap ); bmp_puts( fontspec, &x, &y, bmp_printf_buf ); ) } #if 0 void con_printf( unsigned fontspec, const char * fmt, ... ) { va_list ap; char buf[ 256 ]; static int x = 0; static int y = 32; va_start( ap, fmt ); vsnprintf( buf, sizeof(buf), fmt, ap ); va_end( ap ); const uint32_t pitch = BMPPITCH; uint8_t * vram = bmp_vram(); if( !vram ) return; uint8_t * first_row = vram + y * pitch + x; uint8_t * row = first_row; char * s = buf; char c; const struct font * const font = fontspec_font( fontspec ); while( (c = *s++) ) { if( c == '\n' ) { row = first_row += pitch * font->height; y += font->height; x = 0; bmp_fill( 0, 0, y, 720, font->height ); } else { _draw_char( fontspec, row, c ); row += font->width; x += font->width; } if( x > 720 ) { y += font->height; x = 0; bmp_fill( 0, 0, y, 720, font->height ); } if( y > 480 ) { x = 0; y = 32; bmp_fill( 0, 0, y, 720, font->height ); } } } #endif #if 1 void bmp_hexdump( unsigned fontspec, unsigned x, unsigned y, const void * buf, int len ) { if( len == 0 ) return; // Round up len = (len + 15) & ~15; const uint32_t * d = (uint32_t*) buf; do { bmp_printf( fontspec, x, y, #ifdef CONFIG_VXWORKS // low-res screen, need to use larger font "%08x: %08x %08x %08x %08x : %04x", #else "%08x: %08x %08x %08x %08x %08x %08x %08x %08x : %04x", #endif (unsigned) d, len > 0 ? MEMX(d+0) : 0, len > 4 ? MEMX(d+1) : 0, len > 8 ? MEMX(d+2) : 0, len > 12 ? MEMX(d+3) : 0, #ifndef CONFIG_VXWORKS len > 16 ? MEMX(d+4) : 0, len > 20 ? MEMX(d+5) : 0, len > 24 ? MEMX(d+6) : 0, len > 28 ? MEMX(d+7) : 0, #endif (void*)d - (void*)buf ); y += fontspec_height( fontspec ); #ifdef CONFIG_VXWORKS d += 4; len -= 16; #else d += 8; len -= 32; #endif } while(len > 0); } #endif /** Fill a section of bitmap memory with solid color * Only has a four-pixel resolution in X. */ #ifdef CONFIG_500D // unoptimized version; drawing too fast gives ERR70 void bmp_fill( uint8_t color, int x, int y, int w, int h ) { x = COERCE(x, BMP_W_MINUS, BMP_W_PLUS-1); y = COERCE(y, BMP_H_MINUS, BMP_H_PLUS-1); w = COERCE(w, 0, BMP_W_PLUS-x-1); h = COERCE(h, 0, BMP_H_PLUS-y-1); const uint32_t word = 0 | (color << 24) | (color << 16) | (color << 8) | (color << 0); uint32_t* b = (uint32_t*)bmp_vram(); for (int i = y; i < y+h; i++) { for (int j = x; j < x+w; j+=4) { b[BM(j,i)/4] = word; asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); } } } #else // all other cameras work just fine with fast code /* will allow 1-pixel resolution in X and also enables BMP_FILL_HALFALIGN which is 2-pixel resolution */ //#define BMP_FILL_BYTEALIGN /** Fill a section of bitmap memory with solid color * Only has a four-pixel resolution in X. */ void bmp_fill( uint8_t color, int x, int y, int w, int h ) { #ifdef CONFIG_VXWORKS color = D2V(color); #endif x = COERCE(x, BMP_W_MINUS, BMP_W_PLUS-1); y = COERCE(y, BMP_H_MINUS, BMP_H_PLUS-1); w = COERCE(w, 0, BMP_W_PLUS-x-1); h = COERCE(h, 0, BMP_H_PLUS-y-1); const uint16_t halfColor = ((uint16_t)color << 8) | color; const uint32_t wordColor = ((uint32_t)halfColor << 16) | halfColor; const uint64_t dwordColor = ((uint64_t)wordColor << 32) | wordColor; uint8_t* b = bmp_vram(); /* pre-align the pixels to speed up fill routine below. will draw pixels for byte addresses (if enabled) so the code later can use a optimized dword-store operation. if using x positions and widths that are 8-pixel aligned, this code will not get called. __builtin_expect(exp, result) tells the compiler what we think the result is in most of the cases. this will improve performance by not-jumping around, but continously executing code. will reduce the perfomance impact of the byte-/word- aligning routines when enabled. */ #if defined(BMP_FILL_BYTEALIGN) if(unlikely(x & 1)) { for (int posY = y; posY < y+h; posY++) { *((uint8_t *)&(b[BM(x,posY)])) = color; } x += 1; w -= 1; } if(unlikely(w & 1)) { w -= 1; for (int posY = y; posY < y+h; posY++) { *((uint8_t *)&(b[BM(x+w,posY)])) = color; } } #endif #if defined(BMP_FILL_BYTEALIGN) || defined(BMP_FILL_HALFALIGN) if(unlikely(x & 2)) { for (int posY = y; posY < y+h; posY++) { *((uint16_t *)&(b[BM(x,posY)])) = halfColor; } x += 2; w -= 2; } if(unlikely(w & 2)) { w -= 2; for (int posY = y; posY < y+h; posY++) { *((uint16_t *)&(b[BM(x+w,posY)])) = halfColor; } } #endif /* any 32-bit access necessary? not that complex to handle. STRD in the loop later allows 32-bit aligned addresses, so no special treatment needed to make the memory addresses 64 bit aligned. unfortunately the compiler does not generate STRDs, but STM. tell the compiler, is is unlikely that this condition is true, as many paints may start at e.g. 0 and end with e.g. 720 */ if(unlikely(w & 4)) { /* fill a column with 32 bit writes */ for (int posY = y; posY < y+h; posY++) { *((uint32_t *)&(b[BM(x,posY)])) = wordColor; } x += 4; w -= 4; } /* not needed if BMP_FILL_BYTEALIGN is set, but doesnt hurt. its just to protect from misaligned accesses */ x &= ~3; w &= ~7; /* finally fill with optimized 64 bit writes. planned to let the compiler generate STRD that uses only 2 cpu cycles plus ADD that needs 1. but we only get STMIA. the generated STMIA is also fine, but that uses 4 instead of 3 cpu cycles. still not that bad. */ for (int i = y; i < y+h; i++) { uint32_t buffer = (uint32_t)&(b[BM(x,i)]); uint32_t bufferEnd = (uint32_t)&(b[BM(x+w,i)]); while (buffer < bufferEnd) { *((uint64_t*)buffer) = dwordColor; buffer += 8; } } } #endif /** Draw a picture of the BMP color palette. */ void bmp_draw_palette( void ) { uint32_t x, y, msb, lsb; const uint32_t height = 30; const uint32_t width = 45; for( msb=0 ; msb<16; msb++ ) { for( y=0 ; ysignature != 0x4D42 ) { DebugMsg( DM_MAGIC, 3, "%s: signature %04x", filename, bmp->signature ); int i; for( i=0 ; i<64; i += 16 ) DebugMsg( DM_MAGIC, 3, "%08x: %08x %08x %08x %08x", buf + i, ((uint32_t*)(buf + i))[0], ((uint32_t*)(buf + i))[1], ((uint32_t*)(buf + i))[2], ((uint32_t*)(buf + i))[3] ); goto signature_fail; } // Update the offset pointer to point to the image data // if it is within bounds const unsigned image_offset = (unsigned) bmp->image; if( image_offset > size ) { DebugMsg( DM_MAGIC, 3, "%s: size too large: %x > %x", filename, image_offset, size ); goto offsetsize_fail; } // Since the read was into uncacheable memory, it will // be very slow to access. Copy it into a cached buffer // and release the uncacheable space. if (compression==bmp->compression) { uint8_t * fast_buf = AllocateMemory( size + 32); if( !fast_buf ) goto fail_buf_copy; memcpy(fast_buf, buf, size); bmp = (struct bmp_file_t *) fast_buf; bmp->image = fast_buf + image_offset; free_dma_memory( buf ); return bmp; } else if (compression==1 && bmp->compression==0) { // convert the loaded image into RLE8 uint32_t size_needed = sizeof(struct bmp_file_t); uint8_t* fast_buf; uint32_t x = 0; uint32_t y = 0; uint8_t* gpos; uint8_t count = 0; uint8_t color = 0; bmp->image = buf + image_offset; for (y = 0; y < bmp->height; y++) { uint8_t* pos = bmp->image + y*bmp->width; color = *pos; count = 0; for (x = 0; x < bmp->width; x++) { if (color==(*pos) && count<255) { count++; } else { color = *pos; count = 1; size_needed += 2; } pos++; } if (count!=0) size_needed += 2; // remaining line size_needed += 2; //0000 EOL } size_needed += 2; //0001 EOF fast_buf = AllocateMemory( size_needed ); if( !fast_buf ) goto fail_buf_copy; memcpy(fast_buf, buf, sizeof(struct bmp_file_t)); gpos = fast_buf + sizeof(struct bmp_file_t); for (y = 0; y < bmp->height; y++) { uint8_t* pos = bmp->image + y*bmp->width; color = *pos; count = 0; for (x = 0; x < bmp->width; x++) { if (color==(*pos) && count<255) { count++; } else { gpos[0] = count;gpos[1] = color; color = *pos; count = 1; gpos+=2;} pos++; } if (count!=0) { gpos[0] = count; gpos[1] = color; gpos+=2;} gpos[0] = 0; gpos[1] = 0; gpos+=2; } gpos[0] = 0; gpos[1] = 1; bmp = (struct bmp_file_t *) fast_buf; bmp->compression = 1; bmp->image = fast_buf + sizeof(struct bmp_file_t); bmp->image_size = size_needed; free_dma_memory( buf ); //~ bmp_printf(FONT_SMALL,0,440,"Memory needed %d",size_needed); return bmp; } fail_buf_copy: offsetsize_fail: signature_fail: read_fail: free_dma_memory( buf ); malloc_fail: getfilesize_fail: DebugMsg( DM_MAGIC, 3, "bmp_load failed"); return NULL; } uint8_t* read_entire_file(const char * filename, int* buf_size) { *buf_size = 0; unsigned size; if( FIO_GetFileSize( filename, &size ) != 0 ) goto getfilesize_fail; DEBUG("File '%s' size %d bytes", filename, size); uint8_t * buf = alloc_dma_memory( size ); if( !buf ) { DebugMsg( DM_MAGIC, 3, "%s: alloc_dma_memory failed", filename ); goto malloc_fail; } size_t rc = read_file( filename, buf, size ); if( rc != size ) goto read_fail; *buf_size = size; return buf; //~ fail_buf_copy: read_fail: free_dma_memory( buf ); malloc_fail: getfilesize_fail: DEBUG("failed"); return NULL; } void clrscr() { BMP_LOCK( bmp_fill( 0x0, BMP_W_MINUS, BMP_H_MINUS, BMP_TOTAL_WIDTH, BMP_TOTAL_HEIGHT ); ) } #if 0 // mirror can be NULL void bmp_draw(struct bmp_file_t * bmp, int x0, int y0, uint8_t* const mirror, int clear) { if (!bmp) return; //~ if (!bmp_enabled) return; if (bmp->compression!=0) return; // bmp_draw doesn't support RLE yet uint8_t * const bvram = bmp_vram(); if (!bvram) return; x0 = COERCE(x0, BMP_W_MINUS, BMP_W_PLUS - (int)bmp->width); y0 = COERCE(y0, BMP_H_MINUS, BMP_H_PLUS - (int)bmp->height); uint32_t x,y; for( y=0 ; y < bmp->height; y++ ) { uint8_t * const b_row = (uint8_t*)( bvram + (y + y0) * BMPPITCH ); uint8_t * const m_row = (uint8_t*)( mirror+ (y + y0) * BMPPITCH ); for( x=0 ; x < bmp->width ; x++ ) { if (clear) { uint8_t p = b_row[ x + x0 ]; uint8_t pix = bmp->image[ x + bmp->width * (bmp->height - y - 1) ]; if (pix && p == pix) b_row[x + x0] = 0; } else { if (mirror) { uint8_t p = b_row[ x + x0 ]; uint8_t m = m_row[ x + x0 ]; if (p != 0 && p != 0x14 && p != 0x3 && p != m) continue; } uint8_t pix = bmp->image[ x + bmp->width * (bmp->height - y - 1) ]; b_row[x + x0] = pix; } } } } #endif /* void bmp_draw_scaled(struct bmp_file_t * bmp, int x0, int y0, int xmax, int ymax) { if (!bmp) return; uint8_t * const bvram = bmp_vram(); if (!bvram) return; int x,y; // those sweep the original bmp int xs,ys; // those sweep the BMP VRAM (and are scaled) #ifdef USE_LUT // we better don't use AllocateMemory for LUT (Err 70) static int16_t lut[960]; for (xs = x0; xs < (x0 + xmax); xs++) { lut[xs] = (xs-x0) * bmp->width/xmax; } #endif for( ys = y0 ; ys < (y0 + ymax); ys++ ) { y = (ys-y0)*bmp->height/ymax; uint8_t * const b_row = bvram + ys * BMPPITCH; for (xs = x0; xs < (x0 + xmax); xs++) { #ifdef USE_LUT x = lut[xs]; #else x = (xs-x0)*bmp->width/xmax; #endif uint8_t pix = bmp->image[ x + bmp->width * (bmp->height - y - 1) ]; b_row[ xs ] = pix; } } }*/ // this is slow, but is good for a small number of pixels :) uint8_t bmp_getpixel(int x, int y) { ASSERT(x >= BMP_W_MINUS && x < BMP_W_PLUS) ASSERT(y >= BMP_H_MINUS && y < BMP_H_PLUS) uint8_t * const bvram = bmp_vram(); return bvram[x + y * BMPPITCH]; } /*uint8_t bmp_getpixel_real(int x, int y) { ASSERT(x >= BMP_W_MINUS && x < BMP_W_PLUS) ASSERT(y >= BMP_H_MINUS && y < BMP_H_PLUS) uint8_t * const bvram = bmp_vram_real(); return bvram[x + y * BMPPITCH]; }*/ void bmp_putpixel(int x, int y, uint8_t color) { uint8_t * const bvram = bmp_vram(); if (!bvram) return; x = COERCE(x, BMP_W_MINUS, BMP_W_PLUS-1); y = COERCE(y, BMP_H_MINUS, BMP_H_PLUS-1); bmp_putpixel_fast(bvram, x, y, color); } void bmp_draw_rect(uint8_t color, int x0, int y0, int w, int h) { uint8_t * const bvram = bmp_vram(); if (!bvram) return; draw_line(x0, y0, x0+w, y0, color); draw_line(x0+w, y0, x0+w, y0+h, color); draw_line(x0+w, y0+h, x0, y0+h, color); draw_line(x0, y0, x0, y0+h, color); } int _bmp_draw_should_stop = 0; void bmp_draw_request_stop() { _bmp_draw_should_stop = 1; } #ifdef CONFIG_VXWORKS /** converting dryos palette to vxworks one */ char bmp_lut[80] = { 0x00, 0xff, 0x99, 0x88, 0x77, 0x44, 0x22, 0x22, 0x11, 0x33, 0xDD, 0x33, 0x11, 0x33, 0x55, 0x66, 0x55, 0x77, 0x22, 0x77, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x99, 0x99, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; void set_ml_palette() { if (!DISPLAY_IS_ON) return; int palette[16] = { 0x00870000, // transparent 0x0000FFFF, // 1 - red 0x00CC00FF, // 2 - green 0xFF0000FF, // 3 - blue 0xFFFF00FF, // 4 - cyan 0xFF00FFFF, // 5 - magenta 0x00FFFFFF, // 6 - yellow 0x0090FFFF, // 7 - orange 0x00000080, // 8 - transparent black 0x000000FF, // 9 - black 0x1C1C1CFF, // A - gray 1 0x404040FF, // B - gray 2 0x7F7F7FFF, // C - gray 3 0xAAAAAAFF, // D - gray 4 0xD4D4D4FF, // E - gray 5 0xFFFFFFFF // F - white }; extern int RGB_Palette[]; extern int LCD_Palette[]; extern int PB_Palette[]; if (0) // convert from RGB to PB with Canon code, write result to a file { // if you change RGB palette, run this first to get the PB equivalent (comment out BmpDDev semaphores first) NotifyBox(10000, "%x ", PB_Palette); SetRGBPaletteToDisplayDevice(palette); // problem: this is unsafe to call (race condition with Canon code) FILE* f = FIO_CreateFileEx(CARD_DRIVE"pb.log"); for (int i = 0; i < 16; i++) my_fprintf(f, "0x%08x, ", PB_Palette[i*3 + 2]); FIO_CloseFile(f); } else // use pre-computed PB palette (just send it to digic) { int palette_pb[16] = {0x00fc0000, 0x0346de7f, 0x036dcba1, 0x031a66ea, 0x03a42280, 0x03604377, 0x03cf9a16, 0x0393b94b, 0x00000000, 0x03000000, 0x03190000, 0x033a0000, 0x03750000, 0x039c0000, 0x03c30000, 0x03eb0000}; for (int i = 0; i < 16; i++) { EngDrvOut(0xC0F14080 + i*4, palette_pb[i]); } EngDrvOut(0xC0F14078, 1); } } void set_ml_palette_if_dirty() { if (!DISPLAY_IS_ON) return; extern int PB_Palette[]; if (PB_Palette[15*3+2] == 0x03eb0000) return; BmpDDev_take_semaphore(); set_ml_palette(); PB_Palette[15*3+2] = 0x03eb0000; BmpDDev_give_semaphore(); } void restore_canon_palette() { // unsafe //~ SetRGBPaletteToDisplayDevice(orig_palette); } /* void guess_palette() { // let's try something else: match DryOs palette to VxWorks's on the fly static int ref_r[80] = {254, 234, 0, 0, 163, 31, 0, 1, 234, 0, 185, 27, 200, 0, 201, 209, 232, 216, 0, 231, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 231, 9, 18, 27, 36, 41, 46, 50, 55, 59, 64, 69, 73, 82, 92, 101, 109, 117, 119, 124, 129, 133, 138, 142, 147, 152, 156, 161, 165, 170, 175, 179, 184, 188, 193, 198, 202, 207, 211, 216, 221, 225, 229}; static int ref_g[80] = {254, 235, 0, 0, 56, 187, 153, 172, 0, 66, 186, 34, 0, 0, 0, 191, 0, 94, 62, 109, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9, 18, 27, 36, 41, 46, 50, 55, 59, 64, 69, 73, 82, 92, 101, 110, 117, 119, 124, 129, 133, 138, 142, 147, 152, 156, 161, 165, 170, 175, 178, 184, 188, 192, 198, 202, 206, 210, 216, 221, 225, 230}; static int ref_b[80] = {255, 235, 0, 0, 0, 216, 0, 1, 1, 211, 139, 126, 0, 168, 154, 0, 231, 76, 75, 0, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 232, 9, 18, 27, 36, 41, 46, 50, 55, 59, 64, 69, 73, 82, 92, 101, 109, 117, 119, 124, 129, 133, 138, 142, 147, 152, 156, 161, 165, 170, 174, 178, 184, 188, 193, 198, 202, 207, 211, 216, 221, 224, 229}; extern int RGB_Palette[]; for (int j = 1; j < 80; j++) { int r = ref_r[j]; int g = ref_g[j]; int b = ref_b[j]; int dr = g-r; int db = g-b; int em = 10000; for (int i = 1; i < 16; i++) { int p = RGB_Palette[i]; int R = (p >> 8) & 0xFF; int G = (p >> 16) & 0xFF; int B = (p >> 24) & 0xFF; int DR = G-R; int DB = G-B; int d = (r-R)*(r-R) + (g-G)*(g-G) + (b-B)*(b-B) + (dr-DR)*(dr-DR) + (db-DB)*(db-DB); if (d < em) { bmp_lut[j] = i | (i<<4); em = d; } } } } */ int D2V(unsigned color) { return bmp_lut[MIN(color & 0xFF,79)]; } #endif void bmp_draw_scaled_ex(struct bmp_file_t * bmp, int x0, int y0, int w, int h, uint8_t* const mirror) { if (!bmp) return; _bmp_draw_should_stop = 0; //~ if (!bmp_enabled) return; x0 = COERCE(x0, BMP_W_MINUS, BMP_W_PLUS-1); y0 = COERCE(y0, BMP_H_MINUS, BMP_H_PLUS-1); w = COERCE(w, 0, BMP_W_PLUS-x0-1); h = COERCE(h, 0, BMP_H_PLUS-y0-1); uint8_t * const bvram = bmp_vram(); if (!bvram) return; int x,y; // those sweep the original bmp int xs,ys; // those sweep the BMP VRAM (and are scaled) if (bmp->compression == 0) { #ifdef USE_LUT // we better don't use AllocateMemory for LUT (Err 70) static int16_t lut[960]; for (xs = x0; xs < (x0 + w); xs++) { lut[xs-BMP_W_MINUS] = (xs-x0) * bmp->width/w; } #endif for( ys = y0 ; ys < (y0 + h); ys++ ) { if (_bmp_draw_should_stop) return; y = (ys-y0)*bmp->height/h; #ifdef CONFIG_VXWORKS uint8_t * const b_row = bvram + ys/2 * BMPPITCH; #else uint8_t * const b_row = bvram + ys * BMPPITCH; #endif uint8_t * const m_row = (uint8_t*)( mirror + ys * BMPPITCH ); for (xs = x0; xs < (x0 + w); xs++) { #ifdef USE_LUT x = lut[xs-BMP_W_MINUS]; #else x = (xs-x0)*bmp->width/w; #endif uint8_t pix = bmp->image[ x + bmp->width * (bmp->height - y - 1) ]; if (mirror) { if (pix) m_row[ xs ] = pix | 0x80; uint8_t p = b_row[ xs ]; uint8_t m = m_row[ xs ]; if (p != 0 && p != 0x14 && p != 0x3 && p != m) continue; if ((p == 0x14 || p == 0x3) && pix == 0) continue; } #ifdef CONFIG_VXWORKS char* p = &b_row[ xs/2 ]; SET_4BIT_PIXEL(p, xs, pix); #else b_row[ xs ] = pix; #endif } } } else if (bmp->compression == 1) { uint8_t * bmp_line = bmp->image; // store the start of the line int bmp_y_pos = bmp->height-1; // store the line number for( ys = y0 + h - 1 ; ys >= y0; ys-- ) { if (_bmp_draw_should_stop) return; y = (ys-y0)*bmp->height/h; int ysc = COERCE(ys, BMP_H_MINUS, BMP_H_PLUS); #ifdef CONFIG_VXWORKS uint8_t * const b_row = bvram + ysc/2 * BMPPITCH; #else uint8_t * const b_row = bvram + ysc * BMPPITCH; #endif uint8_t * const m_row = (uint8_t*)( mirror + ysc * BMPPITCH ); while (y != bmp_y_pos) { // search for the next line if (bmp_line[0]!=0) { bmp_line += 2; } else if (bmp_line[1]==0) { bmp_line += 2; bmp_y_pos--; } else if (bmp_line[1]==1) return; if (y<0) return; if (bmp_line>(uint8_t*)(bmp+bmp->image_size)) return; } uint8_t* bmp_col = bmp_line; // store the actual position inside the bitmap int bmp_x_pos_start = 0; // store the start of the line int bmp_x_pos_end = bmp_col[0]; // store the end of the line uint8_t bmp_color = bmp_col[1]; // store the actual color to use for (xs = x0; xs < (x0 + w); xs++) { x = COERCE((int)((xs-x0)*bmp->width/w), BMP_W_MINUS, BMP_W_PLUS-1); while (x>=bmp_x_pos_end) { // skip to this position if (bmp_col>(uint8_t*)(bmp+bmp->image_size)) break; if (bmp_col[0]==0) break; bmp_col+=2; if (bmp_col>(uint8_t*)(bmp+bmp->image_size)) break; if (bmp_col[0]==0) break; bmp_x_pos_start = bmp_x_pos_end; bmp_x_pos_end = bmp_x_pos_start + bmp_col[0]; bmp_color = bmp_col[1]; } if (mirror) { if (bmp_color) m_row[ xs ] = bmp_color | 0x80; uint8_t p = b_row[ xs ]; uint8_t m = m_row[ xs ]; if (p != 0 && p != 0x14 && p != 0x3 && p != m) continue; if ((p == 0x14 || p == 0x3) && bmp_color == 0) continue; } #ifdef CONFIG_VXWORKS char* p = &b_row[ xs/2 ]; SET_4BIT_PIXEL(p, xs, bmp_color); #else b_row[ xs ] = bmp_color; #endif } } } } // built-in fonts found by Pel // http://groups.google.com/group/ml-devel/browse_thread/thread/aec4c80eef1cdd6a // http://chdk.setepontos.com/index.php?topic=6204.0 // quick sanity test int bfnt_ok() { int* codes = (int*) BFNT_CHAR_CODES; int i; for (i = 0; i < 20; i++) if (codes[i] != 0x20+i) return 0; int* off = (int*) BFNT_BITMAP_OFFSET; if (off[0] != 0) return 0; for (i = 1; i < 20; i++) if (off[i] <= off[i-1]) return 0; return 1; } // are all char codes in ascending order, for binary search? uint8_t* bfnt_find_char(int code) { int n = (BFNT_BITMAP_OFFSET - BFNT_CHAR_CODES) / 4; int* codes = (int*) BFNT_CHAR_CODES; int* off = (int*) BFNT_BITMAP_OFFSET; if (code <= 'z') return (uint8_t*) (BFNT_BITMAP_DATA + off[code - 0x20]); int i; for (i = 0; i < n; i++) if (codes[i] == code) return (uint8_t*) (BFNT_BITMAP_DATA + off[i]); return 0; } // returns width int bfnt_draw_char(int c, int px, int py, int fg, int bg) { if (!bfnt_ok()) { bmp_printf(FONT_SMALL, 0, 0, "font addr bad"); return 0; } uint8_t * const bvram = bmp_vram(); uint16_t* chardata = (uint16_t*) bfnt_find_char(c); if (!chardata) return 0; uint8_t* buff = (uint8_t*)(chardata + 5); int ptr = 0; int cw = chardata[0]; // the stored bitmap width int ch = chardata[1]; // the stored bitmap height int crw = chardata[2]; // the displayed character width int xo = chardata[3]; // X offset for displaying the bitmap int yo = chardata[4]; // Y offset for displaying the bitmap int bb = cw / 8 + (cw % 8 == 0 ? 0 : 1); // calculate the byte number per line //~ bmp_printf(FONT_SMALL, 0, 0, "%x %d %d %d %d %d %d", chardata, cw, ch, crw, xo, yo, bb); if (crw+xo > 100) return 0; if (ch+yo > 50) return 0; //~ bmp_fill(bg, px, py, crw+xo+3, 40); int i,j,k; for (i = 0; i < ch; i++) { for (j = 0; j < bb; j++) { for (k = 0; k < 8; k++) { if (j*8 + k < cw) { if ((buff[ptr+j] & (1 << (7-k)))) #ifdef CONFIG_VXWORKS bmp_putpixel_fast(bvram, px+j*8+k+xo, py + (i+yo)*2, fg); #else bmp_putpixel_fast(bvram, px+j*8+k+xo, py+i+yo, fg); #endif } } } ptr += bb; } return crw; } /* int bfnt_draw_char_half(int c, int px, int py, int fg, int bg, int g1, int g2) { if (!bfnt_ok()) { bmp_printf(FONT_SMALL, 0, 0, "font addr bad"); return 0; } uint16_t* chardata = bfnt_find_char(c); if (!chardata) return 0; uint8_t* buff = chardata + 5; int ptr = 0; unsigned int cw = chardata[0]; // the stored bitmap width unsigned int ch = chardata[1]; // the stored bitmap height unsigned int crw = chardata[2]; // the displayed character width unsigned int xo = chardata[3]; // X offset for displaying the bitmap unsigned int yo = chardata[4]; // Y offset for displaying the bitmap unsigned int bb = cw / 8 + (cw % 8 == 0 ? 0 : 1); // calculate the byte number per line //~ bmp_printf(FONT_SMALL, 0, 0, "%x %d %d %d %d %d %d", chardata, cw, ch, crw, xo, yo, bb); if (cw > 100) return 0; if (ch > 50) return 0; static uint8_t tmp[50][25]; int i,j,k; for (i = 0; i < 50; i++) for (j = 0; j < 25; j++) tmp[i][j] = 0; for (i = 0; i < ch; i++) { for (j = 0; j < bb; j++) { for (k = 0; k < 8; k++) { if (j*8 + k < cw) { if ((buff[ptr+j] & (1 << (7-k)))) tmp[COERCE((j*8+k)>>1, 0, 49)][COERCE(i>>1, 0, 24)] ++; } } } ptr += bb; } bmp_fill(bg, px+3, py, crw/2+xo/2+3, 20); for (i = 0; i <= cw/2; i++) { for (j = 0; j <= ch/2; j++) { int c = COLOR_RED; switch (tmp[i][j]) { case 0: case 1: case 2: case 3: c = bg; break; case 4: c = fg; break; } if (c != bg) bmp_putpixel(px+xo/2+i, py+yo/2+j, c); } } return crw>>1; }*/ void bfnt_puts(char* s, int x, int y, int fg, int bg) { while (*s) { x += bfnt_draw_char(*s, x, y, fg, bg); s++; } } /* void bfnt_puts_utf8(int* s, int x, int y, int fg, int bg) { while (*s) { x += bfnt_draw_char(*s, x, y, fg, bg); s++; } }*/ #if 1 void bfnt_test() { while(1) { beep(); canon_gui_disable_front_buffer(); int* codes = (int*) BFNT_CHAR_CODES; static int c = 0; c = (BFNT_BITMAP_OFFSET - BFNT_CHAR_CODES) / 4 - 50; for (int i = 0; i < 5; i++) { for (int j = 0; j < 10; j++) { bfnt_draw_char(codes[c], j*70, i*80+20, COLOR_WHITE, COLOR_BLACK); bmp_printf(FONT_MED, j*70, i*80, "%x", codes[c]); c++; } } msleep(2000); clrscr(); } } #endif void bmp_flip(uint8_t* dst, uint8_t* src, int voffset) { ASSERT(src) ASSERT(dst) if (!dst) return; int i,j; int H_LO = hdmi_code == 5 ? BMP_H_MINUS : 0; int H_HI = hdmi_code == 5 ? BMP_H_PLUS : 480; int W_LO = hdmi_code == 5 ? BMP_W_MINUS : 0; int W_HI = hdmi_code == 5 ? BMP_W_PLUS : 720; for (i = H_LO; i < H_HI; i++) // -30 ... 510 { int i_mod = H_HI + H_LO - i + voffset - 1; // 510 ... -30 while (i_mod < H_LO) i_mod += (H_HI - H_LO); while (i_mod >= H_HI) i_mod -= (H_HI - H_LO); for (j = W_LO; j < W_HI; j++) // -120 ... 840 { dst[BM(j,i)] = src[BM(W_HI + W_LO - j, i_mod)]; // 840 ... -120 } } } void bmp_flip_ex(uint8_t* dst, uint8_t* src, uint8_t* mirror, int voffset) { ASSERT(src) ASSERT(dst) ASSERT(mirror) if (!dst) return; int i,j; int H_LO = hdmi_code == 5 ? BMP_H_MINUS : 0; int H_HI = hdmi_code == 5 ? BMP_H_PLUS : 480; int W_LO = hdmi_code == 5 ? BMP_W_MINUS : 0; int W_HI = hdmi_code == 5 ? BMP_W_PLUS : 720; for (i = H_LO; i < H_HI; i++) // -30 ... 510 { int i_mod = H_HI + H_LO - i + voffset - 1; // 510 ... -30 while (i_mod < H_LO) i_mod += (H_HI - H_LO); while (i_mod >= H_HI) i_mod -= (H_HI - H_LO); for (j = W_LO; j < W_HI; j++) // -120 ... 840 { uint8_t m = mirror[BM(j,i)]; uint8_t b = dst[BM(j,i)]; if (m && (m == b) && !(m & 0x80)) { /* zebras here, do nothing */ } else dst[BM(j,i)] = src[BM(W_HI + W_LO - j, i_mod)]; // 840 ... -120 } } } static void bmp_dim_line(void* dest, size_t n, int even) { ASSERT(dest); int* dst = (int*) dest; int* end = (int*)(dest + n); if (even) { for( ; dst < end; dst++) #ifdef CONFIG_VXWORKS *dst = (*dst & 0x0F0F0F0F) | 0x90909090; #else *dst = (*dst & 0x00FF00FF) | 0x02000200; #endif } else { for( ; dst < end; dst++) #ifdef CONFIG_VXWORKS *dst = (*dst & 0xF0F0F0F0) | 0x09090909; #else *dst = (*dst & 0xFF00FF00) | 0x00020002; #endif } } // this is only used in menu, so only cover the 720x480 part void bmp_dim() { uint32_t* b = (uint32_t *)bmp_vram(); ASSERT(b); if (!b) return; int i; //int j; #ifdef CONFIG_VXWORKS for (i = 0; i < 480; i+=2) { bmp_dim_line(&b[BM(0,i)/4], 360, (i/2)%2); } #else for (i = 0; i < 480; i ++) { bmp_dim_line(&b[BM(0,i)/4], 720, i%2); } #endif } void bmp_make_semitransparent() { uint8_t* b = (uint8_t *)bmp_vram(); ASSERT(b); if (!b) return; int i,j; for (i = BMP_H_MINUS; i < BMP_H_PLUS; i ++) { for (j = BMP_W_MINUS; j < BMP_W_PLUS; j ++) { if (b[BM(j,i)] == COLOR_BLACK || b[BM(j,i)] == 40) b[BM(j,i)] = COLOR_BG; } } } void * bmp_lock = 0; void bmp_init(void* unused) { bmp_lock = CreateRecursiveLock(0); ASSERT(bmp_lock) bvram_mirror_init(); update_vram_params(); } INIT_FUNC(__FILE__, bmp_init);