/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2002 by Linus Nielsen Feltzing * * All files in this archive are subject to the GNU General Public License. * See the file COPYING in the source tree root for full license agreement. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ /* 2005-04-16 Tomas Salfischberger: - New BMP loader function, based on the old one (borrowed a lot of calculations and checks there.) - Conversion part needs some optimization, doing unneeded calulations now. */ #include #include #include #include "debug.h" #include "lcd.h" #include "file.h" #include "config.h" #include "system.h" #include "bmp.h" #include "lcd.h" #ifdef __GNUC__ #define STRUCT_PACKED __attribute__((packed)) #else #define STRUCT_PACKED #pragma pack (push, 2) #endif /* Struct from original code. */ struct Fileheader { unsigned short Type; /* signature - 'BM' */ unsigned long Size; /* file size in bytes */ unsigned short Reserved1; /* 0 */ unsigned short Reserved2; /* 0 */ unsigned long OffBits; /* offset to bitmap */ unsigned long StructSize; /* size of this struct (40) */ unsigned long Width; /* bmap width in pixels */ unsigned long Height; /* bmap height in pixels */ unsigned short Planes; /* num planes - always 1 */ unsigned short BitCount; /* bits per pixel */ unsigned long Compression; /* compression flag */ unsigned long SizeImage; /* image size in bytes */ long XPelsPerMeter; /* horz resolution */ long YPelsPerMeter; /* vert resolution */ unsigned long ClrUsed; /* 0 -> color table size */ unsigned long ClrImportant; /* important color count */ } STRUCT_PACKED; struct rgb_quad { /* Little endian */ unsigned char blue; unsigned char green; unsigned char red; unsigned char reserved; } STRUCT_PACKED; /* big endian functions */ static short readshort(short *value) { unsigned char* bytes = (unsigned char*) value; return bytes[0] | (bytes[1] << 8); } static long readlong(long *value) { unsigned char* bytes = (unsigned char*) value; return (long)bytes[0] | ((long)bytes[1] << 8) | ((long)bytes[2] << 16) | ((long)bytes[3] << 24); } unsigned char brightness(struct rgb_quad color) { return (3 * (unsigned int)color.red + 6 * (unsigned int)color.green + (unsigned int)color.blue) / 10; } /* Function to get a pixel from a line. (Tomas: maybe a better way?) */ inline int getpix(int px, unsigned char *bmpbuf) { int a = (px / 8); int b = (7 - (px % 8)); return (bmpbuf[a] & (1 << b)) != 0; } #if LCD_DEPTH == 16 /* Cheapo 24 -> 16 bit dither */ static unsigned short dither_24_to_16(struct rgb_quad rgb, int row, int col) { static const unsigned char dith[][16] = { { /* 5 bit */ 0,6,1,7, 4,2,5,3, 1,7,0,6, 5,3,4,2 }, { /* 6 bit */ 0,3,0,3, 2,1,2,1, 0,3,0,3, 2,1,2,1 }, }; const int elm = (row & 3) + ((col & 3) << 2); unsigned short color; unsigned b = ((unsigned)rgb.blue + dith[0][elm]) >> 3; if (b > 0x1f) b = 0x1f; unsigned g = ((unsigned)rgb.green + dith[1][elm]) >> 2; if (g > 0x3f) g = 0x3f; unsigned r = ((unsigned)rgb.red + dith[0][elm]) >> 3; if (r > 0x1f) r = 0x1f; color = (unsigned short)(b | (g << 5) | (r << 11)); #if LCD_PIXELFORMAT == RGB565SWAPPED color = swap16(color); #endif return color; } #endif /* LCD_DEPTH == 16 */ /****************************************************************************** * read_bmp_file() * * Reads a monochrome BMP file and puts the data in rockbox format in *bitmap. * *****************************************************************************/ int read_bmp_file(char* filename, struct bitmap *bm, int maxsize, int format) { struct Fileheader fh; int width, height, PaddedWidth, PaddedHeight, dst_width; int fd, row, col, ret; struct rgb_quad palette[256]; int invert_pixel = 0; int numcolors; int depth; int totalsize; char *bitmap = bm->data; unsigned char bmpbuf[LCD_WIDTH*sizeof(struct rgb_quad)]; /* Buffer for one line */ #if LCD_DEPTH != 1 bool transparent = false; #if LCD_DEPTH == 16 /* Should adapt dithering to all native depths */ bool dither = false; if(format & FORMAT_DITHER) { dither = true; format &= ~FORMAT_DITHER; } #endif if(format & FORMAT_TRANSPARENT) { transparent = true; format &= ~FORMAT_TRANSPARENT; } #else format = FORMAT_MONO; #endif fd = open(filename, O_RDONLY); /* Exit if file opening failed */ if (fd < 0) { DEBUGF("error - can't open '%s' open returned: %d\n", filename, fd); return (fd * 10) - 1; } /* read fileheader */ ret = read(fd, &fh, sizeof(struct Fileheader)); if(ret < 0) { close(fd); return (ret * 10 - 2); } if(ret != sizeof(struct Fileheader)) { DEBUGF("error - can't read Fileheader structure."); close(fd); return -3; } /* Exit if too wide */ if (readlong(&fh.Width) > LCD_WIDTH) { DEBUGF("error - Bitmap is too wide (%d pixels, max is %d)\n", readlong(&fh.Width), LCD_WIDTH); close(fd); return -5; } /* Exit if too high */ if (readlong(&fh.Height) > LCD_HEIGHT) { DEBUGF("error - Bitmap is too high (%d pixels, max is %d)\n", readlong(&fh.Height), LCD_HEIGHT); close(fd); return -6; } /* Calculate image size */ height = readlong(&fh.Height); width = readlong(&fh.Width); depth = readshort(&fh.BitCount); /* 4-byte boundary aligned */ PaddedWidth = ((width * depth + 31) / 8) & ~3; #if LCD_DEPTH > 1 if(format == FORMAT_ANY) { if(depth == 1) format = FORMAT_MONO; else format = FORMAT_NATIVE; } #endif /* PaddedHeight is for rockbox format. */ if(format == FORMAT_MONO) { PaddedHeight = (height + 7) / 8; dst_width = width; totalsize = PaddedHeight * dst_width; } else { #if LCD_DEPTH == 2 #if LCD_PIXELFORMAT == VERTICAL_PACKING PaddedHeight = (height + 3) / 4; dst_width = width; #else PaddedHeight = height; dst_width = (width + 3) / 4; #endif #else PaddedHeight = height; dst_width = width; #endif totalsize = PaddedHeight * dst_width * sizeof(fb_data); } /* Check if this fits the buffer */ if (totalsize > maxsize) { DEBUGF("error - Bitmap is too large to fit the supplied buffer: " "%d bytes.\n", (PaddedHeight * dst_width)); close(fd); return -7; } if (depth <= 8) { numcolors = readlong(&fh.ClrUsed); if (numcolors == 0) numcolors = 1 << depth; if(read(fd, palette, numcolors * sizeof(struct rgb_quad)) != numcolors * (int)sizeof(struct rgb_quad)) { DEBUGF("error - Can't read bitmap's color palette\n"); close(fd); return -8; } } /* Use the darker palette color as foreground on mono bitmaps */ if(depth == 1) { if(brightness(palette[0]) < brightness(palette[1])) invert_pixel = 1; } /* Search to the beginning of the image data */ lseek(fd, (off_t)readlong(&fh.OffBits), SEEK_SET); #if LCD_DEPTH == 2 if(format == FORMAT_NATIVE) memset(bitmap, 0, totalsize); #endif #if LCD_DEPTH > 1 fb_data *dest = (fb_data *)bitmap; #endif /* loop to read rows and put them to buffer */ for (row = 0; row < height; row++) { unsigned char *p; /* read one row */ ret = read(fd, bmpbuf, PaddedWidth); if (ret != PaddedWidth) { DEBUGF("error reading image, read returned: %d expected was: " "%d\n", ret, PaddedWidth); close(fd); return -9; } switch(depth) { case 1: #if LCD_DEPTH > 1 if(format == FORMAT_MONO) { #endif /* Mono -> Mono */ for (col = 0; col < width; col++) { ret = getpix(col, bmpbuf) ^ invert_pixel; if (ret) { bitmap[width * ((height - row - 1) / 8) + col] |= 1 << ((height - row - 1) % 8); } else { bitmap[width * ((height - row - 1) / 8) + col] &= ~ 1 << ((height - row - 1) % 8); } } #if LCD_DEPTH == 2 #if LCD_PIXELFORMAT == VERTICAL_PACKING } else { /* Mono -> 2gray (iriver H1xx) */ for (col = 0; col < width; col++) { ret = getpix(col, bmpbuf) ^ invert_pixel; if (ret) dest[((height - row - 1)/4) * width + col] |= 0xC0 >> (2 * (~(height - row - 1) & 3)); } } #else } else { /* Mono -> 2gray (ipod) */ for (col = 0; col < width; col++) { ret = getpix(col, bmpbuf) ^ invert_pixel; if (ret) dest[(height - row - 1) * dst_width + col/4] |= 0xC0 >> (2 * (col & 3)); } } #endif #elif LCD_DEPTH == 16 } else { /* Mono -> RGB16 */ for (col = 0; col < width; col++) { ret = getpix(col, bmpbuf); unsigned short rgb16 = LCD_RGBPACK(palette[ret].red, palette[ret].green, palette[ret].blue); dest[width * (height - row - 1) + col] = rgb16; } } #endif break; case 8: p = bmpbuf; #if LCD_DEPTH > 1 if(format == FORMAT_MONO) { #endif /* 8-bit RGB24 palette -> mono */ for (col = 0; col < width; col++) { struct rgb_quad rgb = palette[*p]; ret = brightness(rgb); if (ret > 96) { bitmap[width * ((height - row - 1) / 8) + col] &= ~ 1 << ((height - row - 1) % 8); } else { bitmap[width * ((height - row - 1) / 8) + col] |= 1 << ((height - row - 1) % 8); } p++; } #if LCD_DEPTH == 2 #if LCD_PIXELFORMAT == VERTICAL_PACKING } else { /* 8-bit RGB24 palette -> 2gray (iriver H1xx) */ for (col = 0; col < width; col++) { struct rgb_quad rgb = palette[*p]; ret = brightness(rgb); dest[((height - row - 1)/4) * width + col] |= (~ret & 0xC0) >> (2 * (~(height - row - 1) & 3)); p++; } } #else } else { /* 8-bit RGB24 palette -> 2gray (ipod) */ for (col = 0; col < width; col++) { struct rgb_quad rgb = palette[*p]; ret = brightness(rgb); dest[(height - row - 1) * dst_width + col/4] |= (~ret & 0xC0) >> (2 * (col & 3)); p++; } } #endif #elif LCD_DEPTH == 16 } else { /* 8-bit RGB24 palette -> RGB16 */ for (col = 0; col < width; col++, p++) { struct rgb_quad rgb = palette[*p]; dest[width * (height - row - 1) + col] = dither ? dither_24_to_16(rgb, row, col) : LCD_RGBPACK(rgb.red, rgb.green, rgb.blue); } } #endif break; case 24: p = bmpbuf; #if LCD_DEPTH > 1 if(format == FORMAT_MONO) { #endif /* RGB24 -> mono */ for (col = 0; col < width; col++) { struct rgb_quad rgb; rgb.red = p[2]; rgb.green = p[1]; rgb.blue = p[0]; ret = brightness(rgb); if (ret > 96) { bitmap[width * ((height - row - 1) / 8) + col] &= ~ 1 << ((height - row - 1) % 8); } else { bitmap[width * ((height - row - 1) / 8) + col] |= 1 << ((height - row - 1) % 8); } p += 3; } #if LCD_DEPTH == 2 #if LCD_PIXELFORMAT == VERTICAL_PACKING } else { /* RGB24 -> 2gray (iriver H1xx) */ for (col = 0; col < width; col++) { struct rgb_quad rgb; rgb.red = p[2]; rgb.green = p[1]; rgb.blue = p[0]; ret = brightness(rgb); dest[((height - row - 1)/4) * width + col] |= (~ret & 0xC0) >> (2 * (~(height - row - 1) & 3)); p += 3; } } #else } else { /* RGB24 -> 2gray (ipod) */ for (col = 0; col < width; col++) { struct rgb_quad rgb; rgb.red = p[2]; rgb.green = p[1]; rgb.blue = p[0]; ret = brightness(rgb); dest[(height - row - 1) * dst_width + col/4] |= (~ret & 0xC0) >> (2 * (col & 3)); p += 3; } } #endif #elif LCD_DEPTH == 16 } else { /* RGB24 -> RGB16 */ for (col = 0; col < width; col++, p += 3) { dest[width * (height - row - 1) + col] = dither ? dither_24_to_16(*(struct rgb_quad *)p, row, col) : LCD_RGBPACK(p[2], p[1], p[0]); } } #endif break; } } close(fd); /* returning image size: */ bm->width = width; bm->height = height; #if LCD_DEPTH > 1 bm->format = format; #endif DEBUGF("totalsize: %d\n", totalsize); return totalsize; /* return the used buffer size. */ }