nds_nflib/source/nf_media.c
Antonio Niño Díaz e1506cc74f library: Cleanup of types
Types like u8 and u16 aren't always a good idea. For example:

    void function(u8 x, u8 y)
    {
        u32 value = (x << 16) | y;
    }

The left shift of x will overflow because x is 8 bits wide. It is better
to make both arguments 32 bit wide.

It may also cause the compiler to introduce bit masking operations at
the caller side because the caller doesn't know how the function
behaves internally.

In order to prevent this kind of issues, it's better to use 32 bit
variables unless there is a very good reason to use smaller types (like
in structs, to save RAM).
2023-05-24 02:53:36 +01:00

200 lines
6.1 KiB
C

// SPDX-License-Identifier: MIT
//
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
//
// NightFox LIB - Include de funciones de carga de archivos multimedia
// http://www.nightfoxandco.com/
#include <stdio.h>
#include <nds.h>
#include "nf_basic.h"
#include "nf_bitmapbg.h"
#include "nf_media.h"
void NF_LoadBMP(const char *file, u32 slot)
{
// Temporary buffers
char *buffer = NULL;
// BMP header
typedef struct {
u32 bmp_size; // Size of the file in bytes
u16 res_a; // Reserved
u16 res_b; // Reserved
u32 offset; // Offset where data starts
u32 header_size; // Size of the header in bytes (40 bytes)
u32 width; // Image width in pixels
u32 height; // Image height in pixels
u16 color_planes; // Number of color planes
u16 bpp; // Bits per pixel
u32 compression; // Compression
u32 raw_size; // Size of raw data after the header
u32 dpi_hor; // Dots per inch (horizontal)
u32 dpi_ver; // Dots per inch (vertical)
u32 pal_colors; // Number of colors in the palette
u32 imp_colors; // Important colors
} bmp_header_info;
bmp_header_info bmp_header = { 0 };
// File path
char filename[256];
// Load BMP file
snprintf(filename, sizeof(filename), "%s/%s.bmp", NF_ROOTFOLDER, file);
FILE *file_id = fopen(filename, "rb");
if (file_id == NULL) // If the file can't be opened
NF_Error(101, filename, 0);
// Read magic string "BM" (0x00 - 0x01)
char magic_id[2];
fread(magic_id, 1, 2, file_id);
if ((magic_id[0] != 'B') || (magic_id[1] != 'M')) // If not a valid BMP file
NF_Error(101, "BMP", 0);
// Read the rest of the BMP header (0x02 - 0x36)
fread((void *)&bmp_header, 1, sizeof(bmp_header), file_id);
// Create a temporary buffer
buffer = malloc(bmp_header.raw_size);
if (buffer == NULL)
NF_Error(102, NULL, bmp_header.raw_size);
// Read data to RAM
fseek(file_id, bmp_header.offset, SEEK_SET);
fread(buffer, 1, bmp_header.raw_size, file_id);
fclose(file_id);
// Free the previous buffer and allocate a new one
free(NF_BG16B[slot].buffer);
u32 size = bmp_header.width * bmp_header.height * 2;
NF_BG16B[slot].buffer = malloc(size);
if (NF_BG16B[slot].buffer == NULL)
NF_Error(102, NULL, size);
switch (bmp_header.bpp)
{
case 8: // 8 bits per pixel
{
// Calculate palette size
u32 colors = (bmp_header.offset - 0x36) / 4; // Stored as RGBA
char *palette = malloc(colors * 4);
if (palette == NULL)
NF_Error(102, NULL, colors * 4);
// Reopen file and read the palette
file_id = fopen(filename, "rb");
if (file_id == NULL)
NF_Error(101, filename, 0);
fseek(file_id, 0x36, SEEK_SET);
fread(palette, 1, colors * 4, file_id);
fclose(file_id);
u32 idx = 0;
// Convert file to 16 bit per pixel
for (u32 y = 0; y < bmp_header.height; y++)
{
for (u32 x = 0; x < bmp_header.width; x++)
{
u32 offset = (((bmp_header.height - 1) - y) * bmp_header.width) + x;
u32 pixel = buffer[idx] << 2;
// Split BGR (not RGB) value
u32 b = palette[pixel] >> 3;
u32 g = palette[pixel + 1] >> 3;
u32 r = palette[pixel + 2] >> 3;
// Pack the components as RGB
pixel = r | (g << 5) | (b << 10) | BIT(15);
NF_BG16B[slot].buffer[offset] = pixel;
idx++;
}
// Align to 4 bytes at the end of the row
while ((idx % 4) != 0)
idx ++;
}
// Free palette from RAM
free(palette);
break;
}
case 16: // 16 bits per pixel
{
u32 idx = 0;
for (u32 y = 0; y < bmp_header.height; y++)
{
for (u32 x = 0; x < bmp_header.width; x++)
{
u32 offset = (((bmp_header.height - 1) - y) * bmp_header.width) + x;
u32 pixel = buffer[idx] + (buffer[idx + 1] << 8);
// Split BGR (not RGB) value
u32 b = pixel & 0x1F;
u32 g = (pixel >> 5) & 0x1F;
u32 r = (pixel >> 10) & 0x1F;
// Pack the components as RGB
pixel = r | (g << 5) | (b << 10) | BIT(15);
NF_BG16B[slot].buffer[offset] = pixel;
idx += 2;
}
// Align to 4 bytes at the end of the row
while ((idx % 4) != 0)
idx ++;
}
break;
}
case 24: // 24 bits per pixel
{
u32 idx = 0;
for (u32 y = 0; y < bmp_header.height; y++)
{
for (u32 x = 0; x < bmp_header.width; x++)
{
u32 offset = (((bmp_header.height - 1) - y) * bmp_header.width) + x;
u32 b = buffer[idx] >> 3;
u32 g = buffer[idx + 1] >> 3;
u32 r = buffer[idx + 2] >> 3;
// Pack the components as RGB
u32 pixel = r | (g << 5) | (b << 10) | BIT(15);
NF_BG16B[slot].buffer[offset] = pixel;
idx += 3;
}
// Align to 4 bytes at the end of the row
while ((idx % 4) != 0)
idx ++;
}
break;
}
default:
NF_Error(106, NULL, bmp_header.bpp);
break;
}
// Save background information
NF_BG16B[slot].size = size;
NF_BG16B[slot].width = bmp_header.width;
NF_BG16B[slot].height = bmp_header.height;
NF_BG16B[slot].inuse = true; // Mark as used
// Free temporary buffer
free(buffer);
}