8086 MS-DOS File .COM / BMP, dimensione del file di output = 2192 byte
Codificatore
Il codificatore è scritto in C. Richiede due argomenti: file di input e file di output. Il file di input è un'immagine RAW RGB 64x64 (che significa semplicemente triplette RGB 4096). Il numero di colori è limitato a 4, quindi la tavolozza può essere il più corta possibile. È molto diretto nelle sue azioni; costruisce semplicemente una tavolozza, racchiude coppie di pixel in byte e la incolla insieme alle intestazioni predefinite e al programma di decodifica.
#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;
}
File di uscita
Il file di output è un file BMP che può essere rinominato .COM ed eseguito in un ambiente DOS. Al momento dell'esecuzione, passerà alla modalità video 13h e visualizzerà l'immagine.
Un file BMP ha una prima intestazione BITMAPFILEHEADER, che contiene tra l'altro il campo ImageOffset, che indica dove iniziano i dati dell'immagine nel file. Dopo questo arriva BITMAPINFOHEADER con varie informazioni di de / codifica, seguite da una tavolozza, se usata. ImageOffset può avere un valore che punta oltre la fine di qualsiasi intestazione, permettendoci di creare uno spazio vuoto per il decodificatore. Circa:
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
Un altro problema è inserire il decodificatore. BITMAPFILEHEADER e BITMAPINFOHEADER possono essere armeggiati per assicurarsi che siano codici macchina legali (che non producono uno stato non recuperabile), ma la tavolozza è più complicata. Ovviamente avremmo potuto allungare artificialmente la palette e inserire lì il codice macchina, ma ho optato per utilizzare invece i campi biXPelsPerMeter e biYPelsPerMeter, il primo per allineare correttamente il codice e il secondo per saltare nel decodificatore. Questi campi avranno ovviamente dei rifiuti, ma qualsiasi visualizzatore di immagini con cui ho provato visualizza bene l'immagine. Tuttavia, la stampa potrebbe produrre risultati particolari.
Per quanto ne so, è conforme agli standard.
Si potrebbe creare un file più breve se l' JMP
istruzione fosse inserita in uno dei campi riservati in BITMAPFILEHEADER. Ciò ci consentirebbe di memorizzare l'altezza dell'immagine come -64 anziché 64, che nel magico paese delle meraviglie dei file BMP significa che i dati dell'immagine vengono archiviati nel modo giusto, il che a sua volta consentirebbe un decodificatore semplificato.
decoder
Nessun trucco particolare nel decodificatore. La tavolozza è popolata dall'encoder e mostrata qui con valori fittizi. Potrebbe essere leggermente più breve se non tornasse in DOS dopo aver premuto un tasto, ma non è stato divertente testarlo. Se lo ritieni necessario, puoi sostituire le ultime tre istruzioni con jmp $
per salvare alcuni byte. (Non dimenticare di aggiornare le intestazioni dei file se lo fai!)
BMP memorizza le palette come BGR ( no terzine RGB), riempite con zero. Ciò rende l'impostazione della palette VGA più fastidiosa del solito. Il fatto che i BMP siano archiviati capovolti non fa che aumentare il sapore (e le dimensioni).
Elencati qui in stile NASM:
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
parte della sfida, e quando lo visualizziamo come.png
ci sono pixel modificati basati su questo.exe
-code. È consentito finché è ancora.png
possibile visualizzarlo? Anche l'immagine in uscita deve avere almeno 4 colori?