8086 MS-DOS .COM 파일 / BMP, 출력 파일 크기 = 2192 바이트
엔코더
인코더는 C로 작성됩니다. 입력 파일과 출력 파일의 두 가지 인수가 필요합니다. 입력 파일은 64x64 RAW RGB 이미지입니다 (즉, 단순히 4096 RGB 3 중입니다). 색상 수는 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;
}
결과물 파일
출력 파일은 .COM으로 이름을 바꾸고 DOS 환경에서 실행할 수있는 BMP 파일입니다. 실행되면 비디오 모드 13h로 변경되고 이미지가 표시됩니다.
BMP 파일에는 첫 번째 헤더 BITMAPFILEHEADER가 있으며, 여기에는 이미지 데이터가 시작되는 파일을 나타내는 ImageOffset 필드가 포함됩니다. 그런 다음 BITMAPINFOHEADER에 다양한 디코드 / 인코딩 정보가 있고 그 뒤에 팔레트가 사용됩니다. ImageOffset은 헤더 끝을 넘는 값을 가질 수 있으므로 디코더가 상주 할 수 있습니다.
BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA
다른 문제는 디코더에 들어가는 것입니다. BITMAPFILEHEADER 및 BITMAPINFOHEADER를 사용하여 합법적 인 기계 코드 (복구 불가능한 상태를 생성하지 않음)인지 확인할 수 있지만 팔레트는 더 까다 롭습니다. 물론 팔레트를 인위적으로 더 길게 만들고 기계 코드를 거기에 넣을 수는 있었지만 대신 biXPelsPerMeter 및 biYPelsPerMeter 필드를 사용하여 코드를 올바르게 정렬하고 후자를 디코더로 건너 뛰었습니다. 이 필드에는 물론 쓰레기가 들어 있지만 테스트 한 이미지 뷰어는 이미지를 잘 표시합니다. 인쇄하면 독특한 결과가 나올 수 있습니다.
내가 아는 한 표준을 준수합니다.
JMP
명령이 BITMAPFILEHEADER의 예약 된 필드 중 하나에 배치 된 경우 더 짧은 파일을 작성할 수 있습니다. 이를 통해 이미지 높이를 64 대신 -64로 저장할 수 있습니다. BMP 파일의 마법의 원더 랜드에서 이미지 데이터가 올바른 방식으로 저장되어 디코더가 간단 해집니다.
디코더
디코더에는 특별한 트릭이 없습니다. 팔레트는 인코더로 채워지고 여기에 더미 값으로 표시됩니다. 키를 눌렀을 때 DOS로 돌아 가지 않으면 약간 짧아 질 수 있지만, 테스트 없이는 재미 있지 않았습니다. 필요하다고 생각되면 마지막 3 개의 명령어를 jmp $
몇 바이트로 대체 할 수 있습니다 . (그렇다면 파일 헤더를 업데이트하는 것을 잊지 마십시오!)
BGR (BMP 같은 저장 팔레트 하지 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
, 챌린지 의 일부에 대해 일종의 코드를 추가하고 코드로 볼.png
때이.exe
코드를 기반으로 수정 된 픽셀이 있다고 가정 해 봅시다 . 여전히.png
볼 수 있는 한 허용 됩니까? 출력 이미지도 4 색 이상이어야합니까?