8086 tệp MS-DOS .COM / BMP, kích thước tệp đầu ra = 2192 byte
Mã hoá
Bộ mã hóa được viết bằng C. Nó có hai đối số: tệp đầu vào và tệp đầu ra. Tệp đầu vào là hình ảnh RAW RGB 64x64 (có nghĩa đơn giản là bộ ba 4096 RGB). Số lượng màu được giới hạn là 4, do đó bảng màu có thể càng ngắn càng tốt. Nó rất đơn giản trong việc làm của nó; nó chỉ đơn thuần xây dựng một bảng màu, gói các cặp pixel thành byte và dán nó cùng với các tiêu đề được tạo sẵn và chương trình giải mã.
#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;
}
Tập tin đầu ra
Tệp đầu ra là tệp BMP có thể được đổi tên thành .COM và chạy trong môi trường DOS. Sau khi thực hiện, nó sẽ chuyển sang chế độ video 13h và hiển thị hình ảnh.
Một tệp BMP có tiêu đề đầu tiên BITMAPFILEHEADER, trong đó có một số thứ khác trong trường ImagePackset, biểu thị nơi dữ liệu hình ảnh bắt đầu. Sau đó, BITMAPINFOHEADER xuất hiện với nhiều thông tin mã hóa khác nhau, theo sau là bảng màu, nếu được sử dụng. ImagePackset có thể có một giá trị nằm ngoài điểm cuối của bất kỳ tiêu đề nào, cho phép chúng tôi tạo khoảng cách cho bộ giải mã cư trú. Roughly:
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
Một vấn đề khác là nhập bộ giải mã. BITMAPFILEHEADER và BITMAPINFOHEADER có thể được sửa đổi để đảm bảo chúng là mã máy hợp pháp (không tạo ra trạng thái không thể phục hồi), nhưng bảng màu phức tạp hơn. Tất nhiên chúng ta có thể làm cho bảng màu dài hơn một cách giả tạo và đặt mã máy ở đó, nhưng tôi đã chọn thay vào đó sử dụng các trường biXPelsPerMeter và biYPelsPerMeter, trước đây để làm cho mã được căn chỉnh chính xác và sau đó nhảy vào bộ giải mã. Các trường này tất nhiên sẽ có rác trong đó, nhưng bất kỳ trình xem ảnh nào tôi đã kiểm tra đều hiển thị hình ảnh tốt. Tuy nhiên, việc in nó có thể tạo ra kết quả đặc biệt.
Theo như tôi biết, nó tuân thủ tiêu chuẩn.
Người ta có thể tạo một tệp ngắn hơn nếu JMP
hướng dẫn được đặt vào một trong các trường dành riêng trong BITMAPFILEHEADER. Điều này sẽ cho phép chúng tôi lưu trữ chiều cao hình ảnh là -64 thay vì 64, trong thế giới thần tiên của các tệp BMP có nghĩa là dữ liệu hình ảnh được lưu trữ đúng cách, từ đó sẽ cho phép bộ giải mã đơn giản hóa.
Bộ giải mã
Không có thủ thuật cụ thể trong bộ giải mã. Bảng màu được điền bởi bộ mã hóa và hiển thị ở đây với các giá trị giả. Nó có thể ngắn hơn một chút nếu nó không quay trở lại DOS khi nhấn phím, nhưng nó không phải là thử nghiệm thú vị nếu không có điều đó. Nếu bạn cảm thấy bạn phải, bạn có thể thay thế ba hướng dẫn cuối cùng jmp $
để lưu một vài byte. (Đừng quên cập nhật tiêu đề tệp nếu bạn làm thế!)
BMP lưu trữ các bảng màu dưới dạng bộ ba BGR ( không phải RGB), được đệm bằng số không. Điều này làm cho việc thiết lập bảng màu VGA trở nên khó chịu hơn bình thường. Việc BMP được lưu trữ lộn ngược chỉ làm tăng thêm hương vị (và kích thước).
Được liệt kê ở đây theo phong cách 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
phần thử thách và khi xem nó dưới dạng các.png
pixel được sửa đổi dựa trên.exe
mã này . Điều này có được phép miễn là.png
chúng ta vẫn có thể xem không? Có phải hình ảnh đầu ra cũng phải có ít nhất 4 màu?