rockbox/firmware/screendump.c

381 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* 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[MAX_PATH];
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*sizeof(fb_data);
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[MAX_PATH];
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 */