open-story-teller/firmware/library/bitmap.c

314 lines
9.5 KiB
C

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "serializers.h"
#include "ost_hal.h"
#include "debug.h"
#ifdef OST_USE_FF_LIBRARY
#include "ff.h"
#include "diskio.h"
typedef FIL file_t;
#else
// Use standard library
typedef FILE *file_t;
typedef int FRESULT;
#define F_OK
#endif
file_t ost_file_open(const char *filename)
{
#ifdef OST_USE_FF_LIBRARY
file_t fil;
FRESULT fr = f_open(&fil, filename, FA_READ);
if (fr != FR_OK)
{
debug_printf("ERROR: f_open %d\n\r", (int)fr);
}
return fil;
#else
return fopen(filename, "r");
#endif
}
static uint8_t bmpImage[512];
static uint8_t decompressed[1024];
static uint8_t palette[16 * 4];
typedef struct
{
uint16_t type; /* Magic identifier */
uint32_t size; /* File size in bytes */
uint16_t reserved1;
uint16_t reserved2;
uint32_t offset; /* Offset to image data, bytes */
} bmp_header_t;
typedef struct
{
uint32_t size; /* Header size in bytes */
uint32_t width;
uint32_t height; /* Width and height of image */
uint16_t planes; /* Number of colour planes */
uint16_t bits; /* Bits per pixel */
uint32_t compression; /* Compression type */
uint32_t imagesize; /* Image size in bytes */
uint32_t xresolution;
uint32_t yresolution; /* Pixels per meter */
uint32_t ncolours; /* Number of colours */
uint32_t importantcolours; /* Important colours */
uint32_t rgb;
uint32_t rgb2;
} bmp_infoheader_t;
static const uint32_t HEADER_SIZE = 14;
static const uint32_t INFO_HEADER_SIZE = 40;
uint8_t parse_bmp(const uint8_t *data, bmp_header_t *header, bmp_infoheader_t *info_header)
{
uint8_t isBmp = 0;
// Header is 14 bytes length
isBmp = (data[0] == 'B') && (data[1] == 'M') ? 1 : 0;
header->size = leu32_get(data + 2);
header->offset = leu32_get(data + 10);
info_header->size = leu32_get(data + HEADER_SIZE);
info_header->width = leu32_get(data + HEADER_SIZE + 4);
info_header->height = leu32_get(data + HEADER_SIZE + 8);
info_header->planes = leu16_get(data + HEADER_SIZE + 12);
info_header->bits = leu16_get(data + HEADER_SIZE + 14);
info_header->compression = leu32_get(data + HEADER_SIZE + 16);
info_header->imagesize = leu32_get(data + HEADER_SIZE + 20);
info_header->xresolution = leu32_get(data + HEADER_SIZE + 24);
info_header->yresolution = leu32_get(data + HEADER_SIZE + 28);
info_header->ncolours = leu32_get(data + HEADER_SIZE + 32);
info_header->importantcolours = leu32_get(data + HEADER_SIZE + 36);
info_header->rgb = leu32_get(data + HEADER_SIZE + 40);
info_header->rgb2 = leu32_get(data + HEADER_SIZE + 44);
return isBmp;
}
void picture_show(const char *filename)
{
file_t fil;
uint32_t offset;
unsigned int br;
fil = ost_file_open(filename); //"0_000_314CBAA1.bmp");
offset = 0;
#ifdef OST_USE_FF_LIBRARY
f_lseek(&fil, offset);
f_read(&fil, bmpImage, 512, &br);
#else
fseek(fil, offset, SEEK_SET);
fread(bmpImage, sizeof(uint8_t), 512, fil);
#endif
int nblines = 0;
bmp_header_t header;
bmp_infoheader_t info_header;
parse_bmp(bmpImage, &header, &info_header);
// Compute some sizes
uint32_t fileSize = header.size;
// uint32_t compressedSize = fileSize - header.offset;
uint32_t paletteSize = header.offset - (HEADER_SIZE + INFO_HEADER_SIZE);
// Copy palette
offset = HEADER_SIZE + INFO_HEADER_SIZE;
memcpy(palette, bmpImage + HEADER_SIZE + INFO_HEADER_SIZE, paletteSize);
offset = header.offset;
uint8_t *compressed = &bmpImage[header.offset]; // start with data just after the BPM header
debug_printf("File size (from header):%d\r\n", (uint32_t)header.size);
debug_printf("File size (from data):%d\r\n", (uint32_t)fileSize);
debug_printf("Data offset:%d\r\n", (uint32_t)header.offset);
debug_printf("Image size:%d\r\n", (uint32_t)info_header.size);
debug_printf("width:%d\r\n", (uint32_t)info_header.width);
debug_printf("height:%d\r\n", (uint32_t)info_header.height);
debug_printf("Planes:%d\r\n", (uint32_t)info_header.planes);
debug_printf("Bits:%d\r\n", (uint32_t)info_header.bits);
debug_printf("Compression:%d\r\n", (uint32_t)info_header.compression); // 2 - 4 bit run length encoding
debug_printf("Image size:%d\r\n", (uint32_t)info_header.imagesize);
debug_printf("X resolution:%d\r\n", (uint32_t)info_header.xresolution);
debug_printf("Y resolution:%d\r\n", (uint32_t)info_header.yresolution);
debug_printf("Colors:%d\r\n", (uint32_t)info_header.ncolours);
debug_printf("Important colors:%d\r\n", (uint32_t)info_header.importantcolours);
debug_printf("RGB :%d\r\n", (uint32_t)info_header.rgb);
debug_printf("RGB2 :%d\r\n", (uint32_t)info_header.rgb2);
// buffer de sortie, bitmap décompressé
memset(decompressed, 0, sizeof(decompressed));
// btea((uint32_t*) bmpImage, -128, key);
uint32_t pixel = 0; // specify the pixel offset
bool end = false;
uint32_t i = 0;
uint32_t totalPixels = 0;
rect_t pos;
pos.x = 0;
pos.y = 0;
pos.width = info_header.width;
pos.height = 1;
do
{
// if we are behond the middle of the buffer, read more data from file
if (i > 256)
{
offset = offset + i;
#ifdef OST_USE_FF_LIBRARY
f_lseek(&fil, offset);
f_read(&fil, bmpImage, 512, &br);
#else
fseek(fil, offset, SEEK_SET);
fread(bmpImage, sizeof(uint8_t), 512, fil);
#endif
compressed = &bmpImage[0];
i = 0;
}
uint8_t rleCmd = compressed[i];
if (rleCmd > 0)
{
uint8_t val = compressed[i + 1];
// repeat number of pixels
for (uint32_t j = 0; j < rleCmd; j++)
{
if ((j & 1) == 0)
{
decompressed[pixel] = (val & 0xF0) >> 4;
}
else
{
decompressed[pixel] = (val & 0x0F);
}
pixel++;
if (pixel > info_header.width)
{
debug_printf("!"); // should be an error here
}
}
i += 2; // jump pair instruction
}
else
{
uint8_t second = compressed[i + 1];
if (second == 0)
{
if (pixel % info_header.width)
{
// end of line
uint32_t lines = pixel / info_header.width;
uint32_t remaining = info_header.width - (pixel - (lines * info_header.width));
pixel += remaining;
}
i += 2;
}
else if (second == 1)
{
end = true;
}
else if (second == 2)
{
// delta N pixels and M lines
pixel += compressed[i + 2] + compressed[i + 3] * info_header.width;
i += 4;
}
else
{
// absolute mode
uint8_t *ptr = &compressed[i + 2];
// repeat number of pixels
for (uint32_t j = 0; j < second; j++)
{
if ((j & 1) == 0)
{
decompressed[pixel] = (*ptr & 0xF0) >> 4;
}
else
{
decompressed[pixel] = (*ptr & 0x0F);
ptr++;
}
pixel++;
if (pixel >= info_header.width)
{
debug_printf("!");
// pixel = 0;
}
}
i += 2 + (second / 2);
// padded in word boundary, jump if necessary
if ((second / 2) % 2)
{
i++;
}
}
if (pixel == info_header.width)
{
// enough pixels to write a line to the screen
ost_display_draw_h_line(pos.y, decompressed, palette);
// debug_printf("POS Y: %d", pos.y);
memset(decompressed, 0, sizeof(decompressed));
// ili9341_write(&pos, decompressed);
// next line...
pos.y++;
totalPixels += info_header.width;
pixel = 0;
nblines++;
}
else if (pixel > info_header.width)
{
debug_printf("!");
// pixel = 0;00000000000000000000000000000000000000
}
}
if (totalPixels > (info_header.width * info_header.height))
{
end = true; // error
}
} while ((offset < fileSize) && !end);
// if (end)
// {
// debug_printf("\r\n>>>>> Decoding error !! <<<<\r\n");
// }
// Fill missing lines
if (nblines < info_header.height)
{
memset(decompressed, 0, sizeof(decompressed));
int missing_lines = (info_header.height - nblines);
for (int i = 0; i < missing_lines; i++)
{
// ili9341_draw_h_line(pos.y, decompressed, palette);
nblines++;
}
}
#ifdef OST_USE_FF_LIBRARY
f_close(&fil);
#else
fclose(fil);
#endif
debug_printf("\r\nNb lines :%d\r\nTotal pixels: %d", (uint32_t)nblines, (uint32_t)totalPixels);
}