8086 MS-DOS .COM-Datei / BMP, Ausgabedateigröße = 2192 Byte
Encoder
Der Encoder ist in C geschrieben. Er benötigt zwei Argumente: Eingabedatei und Ausgabedatei. Die Eingabedatei ist ein 64 x 64 RAW-RGB-Bild (dh es handelt sich einfach um 4096 RGB-Triplets). Die Anzahl der Farben ist auf 4 begrenzt, damit die Palette so kurz wie möglich ist. Es ist sehr unkompliziert in seinen Handlungen; Es erstellt lediglich eine Palette, packt Pixelpaare in Bytes und klebt sie mit vorgefertigten Headern und dem Decoderprogramm zusammen.
#include <stdio.h>
#include <stdlib.h>
#define MAXPAL 4
#define IMAGESIZE 64 * 64
int main(int argc, char **argv)
{
FILE *fin, *fout;
unsigned char *imgdata = malloc(IMAGESIZE * 3), *outdata = calloc(IMAGESIZE / 2, 1);
unsigned palette[MAXPAL] = {0};
int pal_size = 0;
if (!(fin = fopen(argv[1], "rb")))
{
fprintf(stderr, "Could not open \"%s\".\n", argv[1]);
exit(1);
}
if (!(fout = fopen(argv[2], "wb")))
{
fprintf(stderr, "Could not open \"%s\".\n", argv[2]);
exit(2);
}
fread(imgdata, 1, IMAGESIZE * 3, fin);
for (int i = 0; i < IMAGESIZE; i++)
{
// BMP saves the palette in BGR order
unsigned col = (imgdata[i * 3] << 16) | (imgdata[i * 3 + 1] << 8) | (imgdata[i * 3 + 2]), palindex;
int is_in_pal = 0;
for (int j = 0; j < pal_size; j++)
{
if (palette[j] == col)
{
palindex = j;
is_in_pal = 1;
}
}
if (!is_in_pal)
{
if (pal_size == MAXPAL)
{
fprintf(stderr, "Too many unique colours in input image.\n");
exit(3);
}
palindex = pal_size;
palette[pal_size++] = col;
}
// High nibble is left-most pixel of the pair
outdata[i / 2] |= (palindex << !(i & 1) * 4);
}
char BITMAPFILEHEADER[14] = {
0x42, 0x4D, // "BM" magic marker
0x90, 0x08, 0x00, 0x00, // FileSize
0x00, 0x00, // Reserved1
0x00, 0x00, // Reserved2
0x90, 0x00, 0x00, 0x00 // ImageOffset
};
char BITMAPINFOHEADER[40] = {
0x28, 0x00, 0x00, 0x00, // StructSize
0x40, 0x00, 0x00, 0x00, // ImageWidth
0x40, 0x00, 0x00, 0x00, // ImageHeight
0x01, 0x00, // Planes
0x04, 0x00, // BitsPerPixel
0x00, 0x00, 0x00, 0x00, // CompressionType (0 = none)
0x00, 0x00, 0x00, 0x00, // RawImagDataSize (0 is fine for non-compressed,)
0x00, 0x00, 0x00, 0x90, // HorizontalRes
// db 0, 0, 0
// nop
0xEB, 0x1A, 0x90, 0x90, // VerticalRes
// jmp Decoder
// nop
// nop
0x04, 0x00, 0x00, 0x00, // NumPaletteColours
0x00, 0x00, 0x00, 0x00, // NumImportantColours (0 = all)
};
char DECODER[74] = {
0xB8, 0x13, 0x00, 0xCD, 0x10, 0xBA, 0x00, 0xA0, 0x8E, 0xC2, 0xBA,
0xC8, 0x03, 0x31, 0xC0, 0xEE, 0x42, 0xBE, 0x38, 0x01, 0xB1, 0x04,
0xFD, 0x51, 0xB1, 0x03, 0xAC, 0xD0, 0xE8, 0xD0, 0xE8, 0xEE, 0xE2,
0xF8, 0x83, 0xC6, 0x07, 0x59, 0xE2, 0xEF, 0xFC, 0xB9, 0x00, 0x08,
0xBE, 0x90, 0x01, 0xBF, 0xC0, 0x4E, 0xAC, 0xD4, 0x10, 0x86, 0xC4,
0xAB, 0xF7, 0xC7, 0x3F, 0x00, 0x75, 0x04, 0x81, 0xEF, 0x80, 0x01,
0xE2, 0xEE, 0x31, 0xC0, 0xCD, 0x16, 0xCD, 0x20,
};
fwrite(BITMAPFILEHEADER, 1, 14, fout);
fwrite(BITMAPINFOHEADER, 1, 40, fout);
fwrite(palette, 4, 4, fout);
fwrite(DECODER, 1, 74, fout);
// BMPs are stored upside-down, because why not
for (int i = 64; i--; )
fwrite(outdata + i * 32, 1, 32, fout);
fclose(fin);
fclose(fout);
return 0;
}
Ausgabedatei
Die Ausgabedatei ist eine BMP-Datei, die in .COM umbenannt und in einer DOS-Umgebung ausgeführt werden kann. Nach der Ausführung wechselt es in den Videomodus 13h und zeigt das Bild an.
Eine BMP-Datei hat einen ersten Header BITMAPFILEHEADER, der unter anderem das Feld ImageOffset enthält, das angibt, wo in der Datei die Bilddaten beginnen. Danach folgt BITMAPINFOHEADER mit verschiedenen Ent- / Kodierungsinformationen, gefolgt von einer Palette, falls eine verwendet wird. ImageOffset kann einen Wert haben, der über das Ende von Headern hinaus zeigt, sodass wir eine Lücke für den Decoder schließen können. Grob:
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
Ein weiteres Problem ist die Eingabe des Decoders. BITMAPFILEHEADER und BITMAPINFOHEADER können bearbeitet werden, um sicherzustellen, dass es sich um legalen Maschinencode handelt (der keinen nicht wiederherstellbaren Status erzeugt), aber die Palette ist schwieriger. Wir hätten die Palette natürlich künstlich verlängern und den Maschinencode dort platzieren können, aber ich habe mich dafür entschieden, stattdessen die Felder biXPelsPerMeter und biYPelsPerMeter zu verwenden, wobei erstere den Code richtig ausrichten und letztere in den Decoder springen. Diese Felder enthalten dann natürlich Müll, aber jeder Bildbetrachter, mit dem ich getestet habe, zeigt das Bild einwandfrei an. Das Drucken kann jedoch zu besonderen Ergebnissen führen.
Soweit ich weiß, ist es standardkonform.
Man könnte eine kürzere Datei erstellen, wenn die JMP
Anweisung in eines der reservierten Felder in BITMAPFILEHEADER gestellt würde. Dies würde es uns ermöglichen, die Bildhöhe als -64 anstelle von 64 zu speichern, was im magischen Wunderland der BMP-Dateien bedeutet, dass die Bilddaten richtig hoch gespeichert werden, was wiederum einen vereinfachten Decoder ermöglichen würde.
Decoder
Keine besonderen Tricks im Decoder. Die Palette wird vom Encoder ausgefüllt und hier mit Dummy-Werten angezeigt. Es könnte etwas kürzer sein, wenn es bei einem Tastendruck nicht zu DOS zurückkehrt, aber es hat keinen Spaß gemacht, ohne das zu testen. Wenn Sie dies für erforderlich halten, können Sie die letzten drei Anweisungen durch ersetzen jmp $
, um einige Bytes zu sparen. (Vergessen Sie nicht, die Datei-Header zu aktualisieren, wenn Sie dies tun!)
BMP speichert Paletten als BGR- Triplets ( nicht RGB-Triplets), die mit Nullen aufgefüllt sind. Dies macht das Einrichten der VGA-Palette ärgerlicher als gewöhnlich. Die Tatsache, dass BMPs verkehrt herum gelagert werden, trägt nur zum Geschmack (und zur Größe) bei.
Hier im NASM-Stil aufgeführt:
Palette:
db 0, 0, 0, 0
db 0, 0, 0, 0
db 0, 0, 0, 0
db 0, 0, 0, 0
Decoder:
; Set screen mode
mov ax, 0x13
int 0x10
mov dx, 0xa000
mov es, dx
; Prepare to set palette
mov dx, 0x3c8
xor ax, ax
out dx, al
inc dx
mov si, Palette + 2
mov cl, 4
std
pal_loop:
push cx
mov cl, 3
pal_inner:
lodsb
shr al, 1
shr al, 1
out dx, al
loop pal_inner
add si, 7
pop cx
loop pal_loop
cld
; Copy image data to video memory
mov cx, 64 * 64 / 2
mov si, ImageData
mov di, 20160
img_loop:
lodsb
aam 16
xchg al, ah
stosw
test di, 63
jnz skip
sub di, 384
skip:
loop img_loop
; Eat a keypress
xor ax, ax
int 0x16
; Return to DOS
int 0x20
ImageData:
.exe
Teil der Herausforderung eine Art Code hinzu , und wenn wir ihn als betrachten,.png
gibt es modifizierte Pixel, die auf diesem.exe
Code basieren . Ist das erlaubt, solange.png
wir es noch sehen können? Muss das Ausgabebild auch mindestens 4 Farben haben?