D
Hallo Leute und Benutzer'inen des Forum's.
Ich bin neu hier, weil ich ein Spezifisches Problem bei der Dekompression eines 8Bit bmp Files habe.
Ich bin via google auf das Forum gestoßen weil irgendwer ein Padding Problem beim einlesen eines bmp Files hatte.
Das Projekt an dem ich arbeite ist etwas umfangreicher, denn es ist ein Video Spiel für die Playstation 2.
Mich Fasziniert die Hardware der PS2 schon seit 20 Jahren, aber bisher hatte ich keine Zeit so ein Umfangreiches Projekt in die Hand zu nehmen.
Bevor es zu den eigentlichen Problem geht, ich Portiere ein altbekanntes 2D Spiel auf die PS2.
Das Spiel gibt es nur für Windows, aber ich möchte so wenig wie möglich vom Original Spiel verändern. Daher will ich die ordinalen bmp Datei die im 8Bit Format RLE Komprimiert sind beibehalten.
Das Problem ist, das der RLE Algorithmus einige Bilder verstümmelt, einige Bilder werden aber korrekt dargestelt.
Hier ist der C-Code:
#include "rlec.h"
void free_RLE();
uint32_t RLE_getDataSize();
uint16_t RLE_get_height();
void printHeader(_bitmap *image);
uint16_t RLE_get_width();
void RLE_get_pixel_rgb(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a);
int RLEDecompression(const char *filePath);
_bitmap *image = NULL;
uint8_t *pallet = NULL;
uint8_t *data = NULL;
void printHeader(_bitmap *image)
{
fprintf(stdout, "\tMagic number: %c%c\n", image->magicNumber[0], image->magicNumber[1]);
fprintf(stdout, "\tSize: %u\n", image->size);
fprintf(stdout, "\tReserved: %u %u %u %u\n", image->reserved[0], image->reserved[1], image->reserved[2], image->reserved[3]);
fprintf(stdout, "\tStart offset: %u\n", image->startOffset);
fprintf(stdout, "\tHeader size: %u\n", image->headerSize);
fprintf(stdout, "\tWidth: %u\n", image->width);
fprintf(stdout, "\tHeight: %u\n", image->height);
fprintf(stdout, "\tPlanes: %u\n", image->planes);
fprintf(stdout, "\tDepth: %u\n", image->depth);
fprintf(stdout, "\tCompression: %u\n", image->compression);
fprintf(stdout, "\tImage size: %u\n", image->imageSize);
fprintf(stdout, "\tX pixels per meters: %u\n", image->xPPM);
fprintf(stdout, "\tY pixels per meters: %u\n", image->yPPM);
fprintf(stdout, "\tNb of colors used: %u\n", image->nUsedColors);
fprintf(stdout, "\tNb of important colors: %u\n", image->nImportantColors);
}
void printPalette(uint8_t *pallet, int paletteSize)
{
for (int i = 0; i < paletteSize; i += 4) // Palette hat 4 Byte pro Farbe (B, G, R, A)
{
printf("Palette[%d]: B=%u, G=%u, R=%u, A=%u\n", i / 4, pallet[i], pallet[i+1], pallet[i+2], pallet[i+3]);
}
}
void printData(uint8_t *data, int dataSize)
{
for (int i = 0; i < dataSize; i++)
{
printf("Data[%d]: %u\n", i, data[i]);
if(i > 100){
return;
}
}
}
bool validate_row_lengths() {
// Berechnung der tatsächlichen Breite (in Bytes), unter Berücksichtigung des Paddings
int paddedRowSize = ((image->width + 3) / 4) * 4;
// Vergleich der berechneten Größe mit der Bildgröße
if (image->imageSize != paddedRowSize * image->height) {
return false;
}
return true;
}
int RLEDecompression(const char *filePath)
{
uint32_t i, j, total = 0, lecture = 0;
uint8_t couples[2];
FILE* ptrIn = fopen(filePath, "rb");
if (ptrIn == NULL)
{
fprintf(stderr, "Error: Cannot open file %s\n", filePath);
return -1; // oder eine andere geeignete Fehlerbehandlung
}
/* Reading the file */
image = malloc(sizeof(_bitmap));
if (!image)
{
fprintf(stderr, "Error while allocating memory\n");
free_RLE();
fclose(ptrIn);
return -1;
}
memset(image, 0x00, sizeof(_bitmap));
lecture = fread(image, 1, sizeof(_bitmap), ptrIn);
if (lecture != sizeof(_bitmap))
{
fprintf(stderr, "Error while reading data\n");
free_RLE();
fclose(ptrIn);
return -1;
}
/* ------- */
/* Controlling the magic number and the header size */
if (image->magicNumber[0] != 'B' || image->magicNumber[1] != 'M' || image->headerSize != 40 || image->depth != 8 || image->compression != 1)
{
fprintf(stderr, "Error: Incompatible file type\n");
free_RLE();
fclose(ptrIn);
return -1;
}
/* ------- */
/* Controlling the start offset */
if (image->startOffset < sizeof(_bitmap))
{
fprintf(stderr, "Error: Wrong start offset\n");
free_RLE();
fclose(ptrIn);
return -1;
}
/* ------- */
printHeader(image);
/* Skipping data from the beginning to the start offset */
size_t paletteSize = image->startOffset - sizeof(_bitmap);
pallet = malloc(paletteSize);
if (!pallet)
{
fprintf(stderr, "Error while allocating memory\n");
free_RLE();
fclose(ptrIn);
return -1;
}
// Lesen der Palette
size_t bytesRead = fread(pallet, 1, paletteSize, ptrIn);
if (bytesRead != paletteSize)
{
fprintf(stderr, "Error: Palette not completely read\n");
free_RLE();
fclose(ptrIn);
return -1;
}
// Palette ausgeben (debugging)
printPalette(pallet, paletteSize);
/* RLE decompression */
data = malloc(image->width * image->height);
if (!data)
{
fprintf(stderr, "Error while allocating memory\n");
free_RLE();
fclose(ptrIn);
return -1;
}
printData(data, image->width * image->height);
for (i = 0; total < image->imageSize; i++)
{
lecture = fread(&couples, 2, 1, ptrIn);
if (lecture != 1)
{
fprintf(stderr, "Error while reading input data\n");
free_RLE();
fclose(ptrIn);
return -1;
}
if (couples[0])
{
// Standardfall: `couples[0]` gibt an, wie oft `couples[1]` wiederholt wird
for (j = 0; j < couples[0]; j++)
{
data[total] = couples[1];
total++;
}
}
else
{
// Spezialfall: `couples[0] == 0`
switch (couples[1])
{
case 0:
// Ende der Zeile: Bewege den Zeiger zur nächsten Zeile
break;
case 1:
// Ende des Bildes: Beenden der Dekompression
fclose(ptrIn);
return 1; // Erfolgreich dekodiert
case 2:
{
// Delta-Modus: Lese zwei weitere Bytes für den Versatz
uint8_t delta[2];
fread(&delta, 2, 1, ptrIn);
total += delta[0] + delta[1] * image->width;
break;
}
default:
// Unkomprimierte Daten: `couples[1]` gibt die Anzahl an, lese diese Bytes
lecture = fread(&data[total], 1, couples[1], ptrIn);
if (lecture != couples[1])
{
fprintf(stderr, "Error while reading raw input data\n");
free_RLE();
fclose(ptrIn);
return -1;
}
total += couples[1];
// RLE-Längenbyte ist nicht nötig zu verwenden
if (couples[1] & 1)
{
// Falls die Anzahl der Bytes ungerade ist, wird ein Padding-Byte benötigt
fseek(ptrIn, 1, SEEK_CUR);
}
break;
}
}
}
fclose(ptrIn);
return 1;
}
void free_RLE()
{
if (image)
{
free(image);
image = NULL;
}
if (pallet)
{
free(pallet);
pallet = NULL;
}
if (data)
{
free(data);
data = NULL;
}
}
uint16_t RLE_get_height(){
if (image)
{
return image->height;
}else{
return 0;
}
}
uint16_t RLE_get_width(){
if (image)
{
return image->width;
}else{
return 0;
}
}
uint32_t RLE_getDataSize(){
if (data)
{
return image->width * image->height;
}else{
return 0;
}
}
void RLE_get_pixel_rgb(int x, int y, unsigned char *r, unsigned char *g, unsigned char *b, unsigned char *a)
{
uint32_t index = y * image->width + x; // Hole den Pixel-Index
uint8_t colorIndex = data[index]; // Hole den Farbindex aus den Dekompressionsdaten
// Finde den entsprechenden Farbwert in der Palette (4 Byte pro Eintrag)
*b = pallet[colorIndex * 4];
*g = pallet[colorIndex * 4 + 1];
*r = pallet[colorIndex * 4 + 2];
*a = 255; // Im 8-Bit-BMP gibt es oft keine echte Transparenz, also standardmäßig auf 255 setzen
}
In meiner Renderer Klasse wandel ich die Pixeldaten dann in 16Bit Pixel um, weil ich den Alpha Wert benötige:
backgroundPlane* Renderer::addBgPlane(string path, bool transparency)
{
backgroundPlane *plane = new backgroundPlane();
if(!plane)
{
std::cout << "Error in Bg Plane erstellung" << std::endl;
return nullptr;
}
if(path.length() > 0)
{
RLEDecompression((char *)path.c_str());
unsigned char r, g, b, a;
const uint16_t width = RLE_get_width();
const uint16_t height = RLE_get_height();
plane->height = height*SCALE_FACTOR;
plane->width = width*SCALE_FACTOR;
plane->tex_height = height;
plane->tex_width = width;
plane->IMG_data = new unsigned char[width*height*2];
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
RLE_get_pixel_rgb(x, y, &r, &g, &b, &a);
// Da BMP im BGR-Format ist, müssen Rot und Blau getauscht werden
unsigned char temp = r;
r = b;
b = temp;
// Konvertiere RGB in 5-Bit-Komponenten und 1-Bit Alpha (RGBA 1555)
uint16_t r_5bit = (r >> 3) & 0x1F; // 5 MSBs von Rot
uint16_t g_5bit = (g >> 3) & 0x1F; // 5 MSBs von Grün
uint16_t b_5bit = (b >> 3) & 0x1F; // 5 MSBs von Blau
uint16_t a_1bit = (r == 0 && g == 0 && b == 0) ? 0x0 : 0x1; // Alpha 0 für Schwarz, sonst 1
if(transparency == 0)
{
a_1bit = 0x1;
}
// Kombiniere die 1-Bit Alpha, 5-Bit Rot, 5-Bit Grün und 5-Bit Blau in einem 16-Bit-Wert
uint16_t pixel = (a_1bit << 15) | (r_5bit << 10) | (g_5bit << 5) | b_5bit;
// Berechnung des Ziel-Indexes im Puffer nach vertikaler Spiegelung
int mirror_y = height - 1 - y;
int index = (mirror_y * width + x) * 2; // 2 Bytes pro Pixel
// Pixel aufteilen und in den Puffer schreiben (Little Endian)
plane->IMG_data[index] = pixel & 0xFF; // LSB (niedrigstes Byte)
plane->IMG_data[index + 1] = (pixel >> 8) & 0xFF; // MSB (höchstes Byte)
}
}
free_RLE();
}
bgPlanes.push_back(plane);
return bgPlanes.back();
}
Ich sehe gerade keine Möglickeiten hier im Forum Anhäge ein zu fügen, deshalb habe ich das Corpus delicti auf meien Host hochgeladen (der gelbe Rahmen stamt von mir).
https://dl0rcp.darc.de/Bildschirmfoto von 2024-10-03 16-43-55_2.png
Zum vergleich wie es mit 24Bit bmp Datein aus schaut:
https://dl0rcp.darc.de/Bildschirmfoto von 2024-10-03 16-45-19.png
Bei genauerer betrachtung wirkt es so als sei die Textur herangezoomt.
Es muss also kein Problem mit dem RLE Alorythums perse sein.
Aber warum funktiert es mit 24 Bit Bildern und mit 8bit RLE nicht?
Beim Schreiben des Beitrags ist mir noch aufgefallen, dass die Baumreihe und die grüne Wiese via VIF DMA Channel gerendert werden. Spich über die Vector Einheit der PS2.
Die andern Teturen werden über den GIF DMA Chanlel gerednert.
Trozedem sind von 5 GIF übetragungen 3 Kaputt und zwei funktieren und mit 24Bit Werten funktiert es?
Mal sehen was ihr so zu sagen habt...
VG, Denny K.