/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2009 by Jens Arnold * * 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 software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "config.h" #include "screendump.h" #include "rbpaths.h" #include "file.h" #include "general.h" #include "lcd.h" #include "stdlib.h" #include "string.h" #include "system.h" #ifdef HAVE_REMOTE_LCD #include "lcd-remote.h" #endif #if LCD_DEPTH == 16 #define BMP_COMPRESSION 3 /* BI_BITFIELDS */ #define BMP_NUMCOLORS 3 #else /* LCD_DEPTH != 16 */ #define BMP_COMPRESSION 0 /* BI_RGB */ #if LCD_DEPTH <= 8 #ifdef HAVE_LCD_SPLIT #define BMP_NUMCOLORS (2 << LCD_DEPTH) #else #define BMP_NUMCOLORS (1 << LCD_DEPTH) #endif #else /* LCD_DEPTH > 8 */ #define BMP_NUMCOLORS 0 #endif /* LCD_DEPTH > 8 */ #endif /* LCD_DEPTH != 16 */ #define BMP_HEADERSIZE (54 + 4 * BMP_NUMCOLORS) #define BMP_DATASIZE (DUMP_BMP_LINESIZE * (LCD_HEIGHT+LCD_SPLIT_LINES)) #define BMP_TOTALSIZE (BMP_HEADERSIZE + BMP_DATASIZE) static const unsigned char bmpheader[] = { 0x42, 0x4d, /* 'BM' */ LE32_CONST(BMP_TOTALSIZE), /* Total file size */ 0x00, 0x00, 0x00, 0x00, /* Reserved */ LE32_CONST(BMP_HEADERSIZE), /* Offset to start of pixel data */ 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */ LE32_CONST(LCD_WIDTH), /* Width in pixels */ LE32_CONST(LCD_HEIGHT+LCD_SPLIT_LINES), /* Height in pixels */ 0x01, 0x00, /* Number of planes (always 1) */ LE16_CONST(DUMP_BMP_BPP), /* Bits per pixel 1/4/8/16/24 */ LE32_CONST(BMP_COMPRESSION),/* Compression mode */ LE32_CONST(BMP_DATASIZE), /* Size of bitmap data */ 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */ 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */ LE32_CONST(BMP_NUMCOLORS), /* Number of used colours */ LE32_CONST(BMP_NUMCOLORS), /* Number of important colours */ #if LCD_DEPTH == 1 #ifdef HAVE_NEGATIVE_LCD BMP_COLOR(LCD_BL_DARKCOLOR), BMP_COLOR(LCD_BL_BRIGHTCOLOR), #ifdef HAVE_LCD_SPLIT BMP_COLOR(LCD_BL_DARKCOLOR_2), BMP_COLOR(LCD_BL_BRIGHTCOLOR_2), #endif #else /* positive display */ BMP_COLOR(LCD_BL_BRIGHTCOLOR), BMP_COLOR(LCD_BL_DARKCOLOR), #endif /* positive display */ #elif LCD_DEPTH == 2 BMP_COLOR(LCD_BL_BRIGHTCOLOR), BMP_COLOR_MIX(LCD_BL_BRIGHTCOLOR, LCD_BL_DARKCOLOR, 1, 3), BMP_COLOR_MIX(LCD_BL_BRIGHTCOLOR, LCD_BL_DARKCOLOR, 2, 3), BMP_COLOR(LCD_BL_DARKCOLOR), #elif LCD_DEPTH == 16 0x00, 0xf8, 0x00, 0x00, /* red bitfield mask */ 0xe0, 0x07, 0x00, 0x00, /* green bitfield mask */ 0x1f, 0x00, 0x00, 0x00, /* blue bitfield mask */ #endif }; static void (*screen_dump_hook)(int fh) = NULL; void screen_dump(void) { int fd, y; char filename[32]; fb_data *src; #if LCD_DEPTH == 1 unsigned mask; unsigned val; #elif (LCD_DEPTH == 2) && (LCD_PIXELFORMAT != HORIZONTAL_PACKING) int shift; unsigned val; #endif #if LCD_DEPTH <= 8 unsigned char *dst, *dst_end; unsigned char linebuf[DUMP_BMP_LINESIZE]; #elif LCD_DEPTH <= 16 unsigned short *dst, *dst_end; unsigned short linebuf[DUMP_BMP_LINESIZE/2]; #else /* 24bit */ unsigned char *dst, *dst_end; unsigned char linebuf[DUMP_BMP_LINESIZE * 3]; #endif #if CONFIG_RTC create_datetime_filename(filename, HOME_DIR, "dump ", ".bmp", false); #else create_numbered_filename(filename, HOME_DIR, "dump_", ".bmp", 4 IF_CNFN_NUM_(, NULL)); #endif fd = creat(filename, 0666); if (fd < 0) return; if (screen_dump_hook) { screen_dump_hook(fd); } else { if(write(fd, bmpheader, sizeof(bmpheader)) != sizeof(bmpheader)) { close(fd); return; } /* BMP image goes bottom up */ for (y = LCD_HEIGHT - 1; y >= 0; y--) { memset(linebuf, 0, DUMP_BMP_LINESIZE); #if defined(HAVE_LCD_SPLIT) && (LCD_SPLIT_LINES == 2) if (y == LCD_SPLIT_POS - 1) { write(fd, linebuf, DUMP_BMP_LINESIZE); write(fd, linebuf, DUMP_BMP_LINESIZE); } #endif dst = linebuf; #if LCD_DEPTH == 1 dst_end = dst + LCD_WIDTH/2; src = FBADDR(0, y >> 3); mask = BIT_N(y & 7); do { val = (*src++ & mask) ? 0x10 : 0; val |= (*src++ & mask) ? 0x01 : 0; #ifdef HAVE_LCD_SPLIT if (y < LCD_SPLIT_POS) val |= 0x22; #endif *dst++ = val; } while (dst < dst_end); #elif LCD_DEPTH == 2 dst_end = dst + LCD_WIDTH/2; #if LCD_PIXELFORMAT == HORIZONTAL_PACKING src = FBADDR(0, y); do { unsigned data = *src++; *dst++ = ((data >> 2) & 0x30) | ((data >> 4) & 0x03); *dst++ = ((data << 2) & 0x30) | (data & 0x03); } while (dst < dst_end); #elif LCD_PIXELFORMAT == VERTICAL_PACKING src = FBADDR(0, y >> 2); shift = 2 * (y & 3); do { val = ((*src++ >> shift) & 3) << 4; val |= ((*src++ >> shift) & 3); *dst++ = val; } while (dst < dst_end); #elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED src = FBADDR(0, y >> 3); shift = y & 7; do { unsigned data = (*src++ >> shift) & 0x0101; val = (((data >> 7) | data) & 3) << 4; data = (*src++ >> shift) & 0x0101; val |= ((data >> 7) | data) & 3; *dst++ = val; } while (dst < dst_end); #endif #elif LCD_DEPTH == 16 dst_end = dst + LCD_WIDTH; src = FBADDR(0, y); do { #if (LCD_PIXELFORMAT == RGB565SWAPPED) /* iPod LCD data is big endian although the CPU is not */ *dst++ = htobe16(*src++); #else *dst++ = htole16(*src++); #endif } while (dst < dst_end); #elif LCD_DEPTH == 24 dst_end = dst + LCD_WIDTH*3; src = FBADDR(0, y); do { *dst++ = src->b; *dst++ = src->g; *dst++ = src->r; ++src; } while (dst < dst_end); #endif /* LCD_DEPTH */ if(write(fd, linebuf, DUMP_BMP_LINESIZE) != DUMP_BMP_LINESIZE) { close(fd); return; } } } close(fd); } void screen_dump_set_hook(void (*hook)(int fh)) { screen_dump_hook = hook; } #ifdef HAVE_REMOTE_LCD #define RBMP_COMPRESSION 0 /* BI_RGB */ #define RBMP_NUMCOLORS (1 << LCD_REMOTE_DEPTH) #define RBMP_BPP 4 #define RBMP_LINESIZE ((LCD_REMOTE_WIDTH/2 + 3) & ~3) #define RBMP_HEADERSIZE (54 + 4 * RBMP_NUMCOLORS) #define RBMP_DATASIZE (RBMP_LINESIZE * LCD_REMOTE_HEIGHT) #define RBMP_TOTALSIZE (RBMP_HEADERSIZE + RBMP_DATASIZE) static const unsigned char rbmpheader[] = { 0x42, 0x4d, /* 'BM' */ LE32_CONST(RBMP_TOTALSIZE), /* Total file size */ 0x00, 0x00, 0x00, 0x00, /* Reserved */ LE32_CONST(RBMP_HEADERSIZE), /* Offset to start of pixel data */ 0x28, 0x00, 0x00, 0x00, /* Size of (2nd) header */ LE32_CONST(LCD_REMOTE_WIDTH), /* Width in pixels */ LE32_CONST(LCD_REMOTE_HEIGHT), /* Height in pixels */ 0x01, 0x00, /* Number of planes (always 1) */ LE16_CONST(RBMP_BPP), /* Bits per pixel 1/4/8/16/24 */ LE32_CONST(RBMP_COMPRESSION), /* Compression mode */ LE32_CONST(RBMP_DATASIZE), /* Size of bitmap data */ 0xc4, 0x0e, 0x00, 0x00, /* Horizontal resolution (pixels/meter) */ 0xc4, 0x0e, 0x00, 0x00, /* Vertical resolution (pixels/meter) */ LE32_CONST(RBMP_NUMCOLORS), /* Number of used colours */ LE32_CONST(RBMP_NUMCOLORS), /* Number of important colours */ #if LCD_REMOTE_DEPTH == 1 BMP_COLOR(LCD_REMOTE_BL_BRIGHTCOLOR), BMP_COLOR(LCD_REMOTE_BL_DARKCOLOR), #elif LCD_REMOTE_DEPTH == 2 BMP_COLOR(LCD_REMOTE_BL_BRIGHTCOLOR), BMP_COLOR_MIX(LCD_REMOTE_BL_BRIGHTCOLOR, LCD_REMOTE_BL_DARKCOLOR, 1, 3), BMP_COLOR_MIX(LCD_REMOTE_BL_BRIGHTCOLOR, LCD_REMOTE_BL_DARKCOLOR, 2, 3), BMP_COLOR(LCD_REMOTE_BL_DARKCOLOR), #endif }; void remote_screen_dump(void) { int fd, y; char filename[32]; fb_remote_data *src; #if LCD_REMOTE_DEPTH == 1 unsigned mask; unsigned val; #elif LCD_REMOTE_DEPTH == 2 int shift; unsigned val; #endif unsigned char *dst, *dst_end; unsigned char linebuf[RBMP_LINESIZE]; #if CONFIG_RTC create_datetime_filename(filename, "", "rdump ", ".bmp", false); #else create_numbered_filename(filename, "", "rdump_", ".bmp", 4 IF_CNFN_NUM_(, NULL)); #endif fd = creat(filename, 0666); if (fd < 0) return; write(fd, rbmpheader, sizeof(rbmpheader)); /* BMP image goes bottom up */ for (y = LCD_REMOTE_HEIGHT - 1; y >= 0; y--) { memset(linebuf, 0, RBMP_LINESIZE); dst = linebuf; #if LCD_REMOTE_DEPTH == 1 dst_end = dst + LCD_REMOTE_WIDTH/2; src = FBREMOTEADDR(0, y >> 3); mask = BIT_N(y & 7); do { val = (*src++ & mask) ? 0x10 : 0; val |= (*src++ & mask) ? 0x01 : 0; *dst++ = val; } while (dst < dst_end); #elif LCD_REMOTE_DEPTH == 2 dst_end = dst + LCD_REMOTE_WIDTH/2; #if LCD_REMOTE_PIXELFORMAT == VERTICAL_INTERLEAVED src = FBREMOTEADDR(0, (y >> 3)); shift = y & 7; do { unsigned data = (*src++ >> shift) & 0x0101; val = (((data >> 7) | data) & 3) << 4; data = (*src++ >> shift) & 0x0101; val |= ((data >> 7) | data) & 3; *dst++ = val; } while (dst < dst_end); #endif #endif /* LCD_REMOTE_DEPTH */ write(fd, linebuf, RBMP_LINESIZE); } close(fd); } #endif /* HAVE_REMOTE_LCD */