자기 표시 이미지 [닫힘]


11

배경

자동 압축 풀림 .ZIP파일이 있습니다. 일반적으로 확장명을 가지며 .EXE파일을 추출하여 압축을 풉니 다. 그러나 이름을 바꿀 때 .ZIPZIP 추출 소프트웨어로 파일을 열 수 있습니다.

( .EXE파일에는 특정 헤더가 .ZIP필요 하지만 파일에는 특정 트레일러가 필요하므로 .EXE헤더와 .ZIP트레일러 가 모두있는 파일을 만들 수 있기 때문에 가능합니다 .)

당신의 작업 :

"자체 표시"이미지 파일을 작성하는 프로그램을 작성하십시오.

  • 프로그램은 입력으로 64x64 이미지 (4 가지 이상의 색상이 지원되어야 함)와 출력으로 "결합 된"파일을 가져옵니다.
  • 프로그램의 출력 파일은 일반적인 이미지 뷰어에 의해 이미지 파일로 인식됩니다.
  • 이미지 뷰어로 출력 파일을 열면 입력 이미지가 표시됩니다
  • 출력 파일은 모든 운영 체제 또는 컴퓨터 유형에 대한 실행 파일로 인식되어야합니다.

    (흔하지 않은 운영 체제 또는 컴퓨터 용 파일이 생성되는 경우 오픈 소스 PC 에뮬레이터가 있으면 좋을 것입니다. 그러나 반드시 필요한 것은 아닙니다.)

  • 출력 파일을 실행할 때 입력 이미지도 표시됩니다
  • 파일 이름 바꾸기 (예 : .PNG에서 .COM)가 필요할 수 있습니다.
  • 프로그램과 출력 파일이 동일한 OS에서 실행될 필요는 없습니다. 프로그램은 예를 들어 Commodore C64에서 실행될 수있는 Windows 프로그램 및 출력 파일 일 수 있습니다.

승리 기준

  • 가장 작은 출력 파일 을 생성하는 프로그램이 승리합니다
  • 입력 이미지에 따라 출력 파일의 크기가 다른 경우 (예 : 프로그램이 이미지를 압축하기 때문에) 최대 4 가지 색상으로 64x64 이미지를 나타내는 프로그램에서 생성 할 수 있는 가장 큰 출력 파일

그건 그렇고

StackOverflow 에서이 질문을 읽을 때 다음 프로그래밍 퍼즐에 대한 아이디어가있었습니다 .


우승 조건 태그를 추가했습니다 (metagolf와 함께 코드 도전-가장 짧은 출력). 입력 64x64 이미지는 이미지가 있습니까? 또한 이미지를 볼 때 이미지 자체가 같아야합니까? 또는 출력 이미지와 입력 이미지가 다를 수 있습니까? 좀 더 구체적으로 말하면 .exe, 챌린지 의 일부에 대해 일종의 코드를 추가하고 코드로 볼 .png때이 .exe코드를 기반으로 수정 된 픽셀이 있다고 가정 해 봅시다 . 여전히 .png볼 수 있는 한 허용 됩니까? 출력 이미지도 4 색 이상이어야합니까?
케빈 Cruijssen

2
"공통 이미지 뷰어"는 어떻게 정의합니까? 예를 들어 HTML "코드"가있는 인터넷 브라우저는 계산됩니까?
Jo King

@KevinCruijssen 이미지 파일로 해석 될 때 출력 파일은 입력 파일과 동일한 이미지를 나타내야합니다. 픽셀의 너비와 높이는 동일하며 각 픽셀의 색상은 동일해야합니다. 파일 형식이 정확히 동일한 색상 표를 지원하지 않는 경우 각 픽셀의 색상은 최대한 비슷해야합니다. 실행 파일로 해석되는 파일도 마찬가지입니다. 출력 파일이 "전체 화면"프로그램을 나타내는 경우 화면의 아무 곳이나 (가운데, 왼쪽 위 가장자리 등) 이미지를 전체 화면 크기로 늘릴 수 있습니다.
Martin Rosenau 2016 년

1
@JoKing "공통 이미지 뷰어로 인식"은 사전 설치된 소프트웨어 (HTML 등)가 설치된 대부분의 컴퓨터에서 파일 형식을 읽거나 많은 사용자가 무료 도구를 다운로드하여 파일을 볼 수 있음을 의미합니다 ( PDF와 같은). 나는 HTML + 자바 스크립트 코드, 그러나, "이미지 뷰어"해야으로 볼 수 있다는 말을 하지 코드를 실행! 따라서 웹 브라우저는 "이미지 뷰어"라고 할 수 있지만이 경우 HTML은 "코드"가 아닙니다. 또는 HTML + JS는 "코드"라고 말할 수 있지만이 경우 웹 브라우저는 "이미지 뷰어"가 아닙니다.
Martin Rosenau 2016 년

2
그러한 흥미로운 질문이 종결되는 것은 슬픈 일입니다. 내가 이해하는 한 질문을 다시 열기 전에 모든 문제를 해결해야합니다. 주석의 주요 사항은 "일반적인 이미지 뷰어"라는 용어로, 모호하기에 충분히 흐릿하고 실행 가능한 코드의 존재에 의해 변경되지 않은 상태 (@KevinCruijssen의 우려에 따라)로 표시되는 이미지는 설명 할 가치가 있습니다. . 이러한 문제를 해결하는 편집으로 충분합니까? (나는 "네 가지 색 네 가지 색"모호성을 이해하지 못한다고 고백한다.)
gastropner

답변:


5

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:

좋은. 나는 또한 BMP / MS-DOS COM 쌍에 대해 생각하고 있었다. 일주일 이내에 답변이 없으면 구현했을 것입니다. 그러나 10K 이상이 필요했을 것입니다. 레지스터가 0으로 초기화되었다고 가정하지 않았기 때문에 파일 오프셋 2에 점프 명령을 배치했을 것입니다.이 필드는 BMP 파일에서 "파일 크기"로 해석되므로 "파일 크기"필드가 올바른 파일 크기를 나타내도록 BMP 파일을 "더미"바이트로 채워야합니다.
Martin Rosenau

@MartinRosenau 사실에 있었다 하지 난 보통 (에 따라 수행하는 것이 레지스터 값의 일부 가정 fysnet.net/yourhelp.htm를 헤더가 레지스터 소지품 때문에), 및 necessating은 PSP의 경우에도 첫 번째 바이트, int 0x20이상 ret.
gastropner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.