Hình ảnh tự hiển thị [đóng]


11

Lý lịch

Có những .ZIPtập tin tự giải nén . Thông thường, chúng có phần mở rộng .EXE(và bằng cách thực thi tệp, chúng sẽ được trích xuất) nhưng khi đổi tên chúng thành .ZIP, bạn có thể mở tệp bằng một số phần mềm giải nén ZIP.

(Điều này là có thể bởi vì .EXEcác tệp yêu cầu một tiêu đề nhất định nhưng .ZIPcác tệp yêu cầu một đoạn giới thiệu nhất định để có thể xây dựng một tệp có cả .EXEtiêu đề và .ZIPđoạn giới thiệu.)

Nhiệm vụ của bạn:

Tạo một chương trình tạo tập tin hình ảnh "tự hiển thị":

  • Chương trình sẽ lấy một số hình ảnh 64x64 (được hỗ trợ ít nhất 4 màu) làm đầu vào và một số tệp "kết hợp" làm đầu ra
  • Tệp đầu ra của chương trình sẽ được nhận dạng là tệp hình ảnh bởi những người xem hình ảnh phổ biến
  • Khi mở tệp đầu ra bằng trình xem ảnh, hình ảnh đầu vào sẽ được hiển thị
  • Tệp đầu ra cũng sẽ được công nhận là tệp thực thi cho bất kỳ loại hệ điều hành hoặc máy tính nào

    (Nếu một tệp cho một hệ điều hành hoặc máy tính không phổ biến được tạo ra, sẽ rất tuyệt nếu trình giả lập PC nguồn mở tồn tại. Tuy nhiên, điều này là không bắt buộc.)

  • Khi thực hiện tệp đầu ra, hình ảnh đầu vào cũng sẽ được hiển thị
  • Có khả năng thu đổi tên tập tin (ví dụ từ .PNGđến .COM) là cần thiết
  • Không yêu cầu chương trình và tệp đầu ra của nó chạy trên cùng một hệ điều hành; ví dụ, chương trình có thể là một chương trình Windows và các tệp đầu ra có thể được thực thi trên Commodore C64.

Tiêu chí chiến thắng

  • Chương trình tạo ra tệp đầu ra nhỏ nhất sẽ thắng
  • Nếu kích thước của tệp đầu ra khác nhau tùy thuộc vào hình ảnh đầu vào (ví dụ vì chương trình nén hình ảnh), thì tệp đầu ra lớn nhất có thể được tạo bởi chương trình đại diện cho hình ảnh 64x64 với số lượng tối đa 4 màu

Nhân tiện

Tôi đã có ý tưởng cho câu đố lập trình sau đây khi đọc câu hỏi này trên StackOverflow.


Tôi đã thêm các thẻ điều kiện chiến thắng (thử thách mã kết hợp với metagolf - đầu ra ngắn nhất). Đối với hình ảnh 64x64 đầu vào, bạn có một số hình ảnh ví dụ không? Ngoài ra, hình ảnh có phải giống nhau khi xem không? Hoặc hình ảnh đầu ra và hình ảnh đầu vào có thể khác nhau? Để cụ thể hơn: giả sử chúng ta thêm một số loại mã cho .exephần thử thách và khi xem nó dưới dạng các .pngpixel được sửa đổi dựa trên .exemã này . Điều này có được phép miễn là .pngchú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?
Kevin Cruijssen

2
Làm thế nào để bạn xác định "trình xem hình ảnh phổ biến"? Ví dụ: trình duyệt internet có "mã" HTML không?
Jo King

@KevinCruijssen Khi được hiểu là tệp hình ảnh, tệp đầu ra sẽ biểu thị cùng một hình ảnh với tệp đầu vào: Cùng chiều rộng và chiều cao tính bằng pixel và mỗi pixel sẽ có cùng màu. Nếu các định dạng tệp không hỗ trợ chính xác cùng một bảng màu, màu của mỗi pixel sẽ càng gần càng tốt. Điều này cũng đúng với tệp được hiểu là tệp thực thi. Nếu tệp đầu ra đại diện cho chương trình "toàn màn hình", nó có thể hiển thị hình ảnh ở bất cứ đâu trên màn hình (ở giữa, cạnh trên bên trái, ...) hoặc kéo dài nó tới kích thước toàn màn hình.
Martin Rosenau

1
@JoKing "Được công nhận bởi những người xem hình ảnh phổ biến" có nghĩa là định dạng tệp có thể được đọc bởi hầu hết các máy tính có phần mềm được cài đặt sẵn (như HTML) hoặc rất nhiều người dùng tải xuống một công cụ miễn phí để xem tệp ( chẳng hạn như PDF). Tôi có thể nói rằng HTML + JavaScript có thể được xem là mã, tuy nhiên, "trình xem hình ảnh" không nên thực thi mã! Vì vậy, nó sẽ được phép nói trình duyệt web là "trình xem hình ảnh", nhưng trong trường hợp này HTML không phải là "mã". Hoặc bạn có thể nói rằng HTML + JS là "mã", nhưng trong trường hợp này, trình duyệt web không phải là "trình xem hình ảnh".
Martin Rosenau

2
Thật buồn khi thấy một câu hỏi thú vị như vậy đóng lại. Theo tôi hiểu, bất kỳ mối quan tâm nào cũng cần được giải quyết trước khi mở lại một câu hỏi. Những điều chính trong các bình luận là thuật ngữ "trình xem ảnh chung", đủ mơ hồ để không rõ ràng và hình ảnh được hiển thị ở trạng thái (theo mối quan tâm của @ KevinCruijssen) không bị thay đổi bởi sự hiện diện của mã thực thi là đáng để làm rõ . Một chỉnh sửa giải quyết những mối quan tâm là đủ? (Tôi thú nhận với không hiểu "là bốn màu bốn màu" mơ hồ.)
gastropner

Câu trả lời:


5

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 JMPhướ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:

Đẹp. Tôi cũng đã suy nghĩ về cặp BMP / MS-DOS COM; Tôi sẽ thực hiện nó nếu không có câu trả lời trong vòng một tuần. Tuy nhiên, tôi sẽ cần nhiều hơn 10K: Vì tôi không cho rằng các thanh ghi được khởi tạo bằng 0, nên tôi đã đặt một lệnh nhảy ở tệp bù 2. Và vì trường này được hiểu là "kích thước tệp" trong tệp BMP, Tôi sẽ phải điền vào tệp BMP bằng các byte "giả" để đảm bảo trường "kích thước tệp" thể hiện kích thước tệp chính xác.
Martin Rosenau

@MartinRosenau Tôi thực sự phải không thừa nhận một số giá trị đăng ký mà tôi thường làm (theo fysnet.net/yourhelp.htm ), kể từ khi các tiêu đề clobber đăng ký, và thậm chí các byte đầu tiên của PSP, necessating int 0x20qua ret.
dạ dày
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.