8086 MS-DOS .COM файл / BMP, розмір вихідного файлу = 2192 байт
Енкодер
Кодер написано в C. Це потребує двох аргументів: вхідний файл і вихідний файл. Вхідний файл - це зображення формату RGB розміром 64x64 (це означає, що це просто 4096 RGB-трійки). Кількість кольорів обмежена 4, так що палітра може бути якомога коротшою. Він дуже прямий у своїх діях; він просто будує палітру, запаковує піксельні пари в байти і склеює їх заздалегідь зробленими заголовками та програмою декодера.
#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;
}
Вихідний файл
Вихідний файл - це BMP-файл, який можна перейменувати .COM і запустити в середовищі DOS. Після її виконання він перейде в режим відеозйомки 13h та відобразить зображення.
Файл BMP має перший заголовок BITMAPFILEHEADER, який містить серед іншого поле ImageOffset, яке позначає, звідки у файлі починаються дані зображення. Після цього надходить BITMAPINFOHEADER з різною інформацією про декодування / кодування з наступною палітрою, якщо така використовується. ImageOffset може мати значення, яке вказує за межі кінця будь-яких заголовків, що дозволяє нам зробити пробіл для декодера для проживання. Приблизно:
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
Ще одна проблема - ввести декодер. BITMAPFILEHEADER та BITMAPINFOHEADER можна зафіксувати, щоб переконатися, що вони є юридичним машинним кодом (який не створює стан, який не підлягає відновленню), але палітра є складнішою. Ми, звичайно, могли зробити палітру штучно довшою і помістили машинний код туди, але я вирішив замість цього використовувати поля biXPelsPerMeter та biYPelsPerMeter, перші для правильного вирівнювання коду, а другі для стрибка в декодер. Ці поля, звичайно, тоді містять сміття в них, але будь-який переглядач зображень, який я протестував, показує зображення добре. Однак друк може призвести до особливих результатів.
Це, наскільки я знаю, відповідає стандартам.
Можна створити коротший файл, якби JMP
інструкція була розміщена в одному із зарезервованих полів BITMAPFILEHEADER. Це дозволило б нам зберегти висоту зображення як -64 замість 64, що в магічній країні чудес файлів BMP означає, що дані зображення зберігаються правильним шляхом, що, в свою чергу, дозволить спростити декодер.
Дешифратор
Ніяких особливих хитрощів у декодері. Палітра заповнюється кодером і тут показана фіктивними значеннями. Це може бути трохи коротше, якби він не повернувся до DOS після натискання клавіші, але без цього не було забавного тестування. Якщо вам здається, що ви повинні, ви можете замінити останні три інструкції, jmp $
щоб зберегти кілька байт. (Не забудьте оновити заголовки файлів, якщо це потрібно!)
BMP зберігає палітри у вигляді трійок BGR ( не RGB), оббитих нулями. Це робить налаштування палітри VGA більш дратівливою, ніж зазвичай. Те, що BMP зберігаються догори дном, лише додає смаку (і розміру).
Перелічено тут у стилі 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
частини виклику, і при перегляді його як a.png
є модифіковані пікселі на основі цього.exe
коду. Чи дозволено це до тих пір, поки це ще.png
ми можемо переглянути? Чи має також вихідне зображення мати принаймні 4 кольори?