Gambar tampilan sendiri [tertutup]

11

Latar Belakang

Ada .ZIPfile yang mengekstraksi sendiri . Biasanya mereka memiliki ekstensi .EXE(dan dengan mengeksekusi file mereka akan diekstraksi) tetapi ketika mengubah nama mereka .ZIP, Anda dapat membuka file dengan beberapa perangkat lunak pengekstrak ZIP.

(Ini dimungkinkan karena .EXEfile memerlukan header tertentu tetapi .ZIPfile memerlukan trailer tertentu sehingga memungkinkan untuk membuat file yang keduanya memiliki .EXEheader dan .ZIPtrailer.)

Tugas Anda:

Buat program yang membuat file gambar "menampilkan sendiri":

  • Program harus mengambil beberapa gambar 64x64 (setidaknya 4 warna harus didukung) sebagai input dan beberapa file "gabungan" sebagai output
  • File keluaran program harus diakui sebagai file gambar oleh pemirsa gambar biasa
  • Saat membuka file output dengan penampil gambar, gambar input harus ditampilkan
  • File output juga harus diakui sebagai file yang dapat dieksekusi untuk semua sistem operasi atau jenis komputer

    (Jika file untuk sistem operasi atau komputer yang tidak biasa diproduksi, alangkah baiknya jika emulator PC open-source ada. Namun, ini tidak diperlukan.)

  • Saat mengeksekusi file output, gambar input juga harus ditampilkan
  • Mungkin mengganti nama file (misalnya dari .PNGke .COM) diperlukan
  • Tidak diperlukan bahwa program dan file outputnya dijalankan pada OS yang sama; program misalnya dapat berupa program Windows dan file output yang dapat dieksekusi pada Commodore C64.

Kriteria menang

  • Program yang menghasilkan file keluaran terkecil akan menang
  • Jika ukuran file output berbeda tergantung pada gambar input (misalnya karena program kompres gambar), file output terbesar yang mungkin dibuat oleh program yang mewakili gambar 64x64 dengan hingga 4 warna jumlah

Ngomong-ngomong

Saya punya ide untuk teka-teki pemrograman berikut ketika membaca pertanyaan ini di StackOverflow.

Martin Rosenau
sumber
Saya telah menambahkan tag kondisi yang menang (tantangan kode dalam kombinasi dengan metagolf - output terpendek). Sedangkan untuk input gambar 64x64, apakah Anda memiliki beberapa contoh gambar? Juga, apakah gambar itu sendiri harus sama ketika dilihat? Atau dapatkah gambar output dan gambar input berbeda? Agar lebih konkret: katakanlah kita menambahkan beberapa jenis kode untuk .exebagian dari tantangan, dan ketika melihatnya sebagai .pngada piksel yang dimodifikasi berdasarkan pada .exe-kode ini . Apakah ini diizinkan selama masih .pngbisa kita lihat? Apakah gambar output juga harus memiliki setidaknya 4 warna?
Kevin Cruijssen
2
Bagaimana Anda mendefinisikan "penampil gambar umum"? Misalnya, apakah browser internet dengan "kode" HTML menghitung?
Jo King
@KevinCruijssen Ketika ditafsirkan sebagai file gambar, file output harus mewakili gambar yang sama dengan file input: Lebar dan tinggi dalam piksel yang sama dan setiap piksel harus memiliki warna yang sama. Jika format file tidak mendukung palet warna yang sama persis, warna setiap pixel harus sedekat mungkin. Hal yang sama berlaku untuk file yang ditafsirkan sebagai file yang dapat dieksekusi. Jika file output mewakili program "layar penuh", mungkin menampilkan gambar di mana saja di layar (tengah, tepi kiri atas, ...) atau meregangkannya ke ukuran layar penuh.
Martin Rosenau
1
@JoKing "Diakui oleh pemirsa gambar umum" berarti bahwa format file dapat dibaca oleh sebagian besar komputer dengan perangkat lunak yang sudah diinstal sebelumnya (seperti HTML) atau bahwa banyak pengguna mengunduh alat gratis untuk melihat file ( seperti PDF). Saya akan mengatakan bahwa HTML + JavaScript dapat dilihat sebagai kode, namun, "penampil gambar" tidak boleh mengeksekusi kode! Jadi boleh dikatakan bahwa browser web adalah "penampil gambar", tetapi dalam hal ini HTML bukan "kode". Atau Anda dapat mengatakan bahwa HTML + JS adalah "kode", tetapi dalam hal ini browser web bukan "penampil gambar".
Martin Rosenau
2
Sedih melihat pertanyaan yang menarik ditutup. Sejauh yang saya mengerti, masalah apa pun harus diatasi sebelum membuka kembali pertanyaan. Hal-hal utama dalam komentar adalah istilah "penampil gambar umum", yang cukup kabur untuk menjadi ambigu, dan gambar yang ditampilkan dalam keadaan (sesuai keprihatinan @ KevinCruijssen) tidak berubah dengan adanya kode yang dapat dieksekusi yang layak untuk diklarifikasi . Apakah suntingan untuk mengatasi masalah itu sudah cukup? (Saya akui tidak mengerti ambiguitas "empat warna empat warna".)
gastropner

Jawaban:

5

8086 MS-DOS .COM file / BMP, ukuran file output = 2192 byte

Encoder

Encoder ditulis dalam C. Dibutuhkan dua argumen: File input dan file output. File input adalah gambar RGB RAW 64x64 (berarti hanya 4096 RGB triplet RGB). Jumlah warna dibatasi hingga 4, sehingga palet bisa sesingkat mungkin. Hal ini sangat mudah dilakukan; itu hanya membangun palet, mengemas pasangan piksel menjadi byte dan menempelkannya bersama dengan header yang dibuat sebelumnya dan program dekoder.

#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;
}

Berkas keluaran

File output adalah file BMP yang dapat diubah namanya .COM dan dijalankan di lingkungan DOS. Setelah dieksekusi, itu akan berubah ke mode video 13j dan menampilkan gambar.

File BMP memiliki BITMAPFILEHEADER header pertama, yang berisi antara lain bidang ImageOffset, yang menunjukkan di mana dalam file data gambar dimulai. Setelah ini datang BITMAPINFOHEADER dengan berbagai informasi de- / encoding, diikuti oleh palet, jika digunakan. ImageOffset dapat memiliki nilai yang menunjuk di luar ujung header apa pun, memungkinkan kami untuk membuat celah bagi decoder untuk berada. Secara kasar:

BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA

Masalah lain adalah memasukkan decoder. BITMAPFILEHEADER dan BITMAPINFOHEADER dapat dipecahkan untuk memastikan kode mesin legal (yang tidak menghasilkan keadaan tidak dapat dipulihkan), tetapi paletnya lebih rumit. Kita tentu saja dapat membuat palet secara artifisial lebih lama, dan menempatkan kode mesin di sana, tetapi saya memilih untuk menggunakan bidang biXPelsPerMeter dan biYPelsPerMeter, yang pertama membuat kode disejajarkan dengan benar, dan yang terakhir untuk melompat ke decoder. Bidang-bidang ini tentu saja akan memiliki sampah di dalamnya, tetapi setiap penampil gambar yang saya uji dengan menampilkan gambar baik-baik saja. Mencetaknya mungkin menghasilkan hasil yang aneh.

Sejauh yang saya tahu, standar-patuh.

Seseorang dapat membuat file yang lebih pendek jika JMPinstruksi dimasukkan ke dalam salah satu bidang yang disediakan di BITMAPFILEHEADER. Ini akan memungkinkan kita untuk menyimpan ketinggian gambar sebagai -64 dan bukannya 64, yang di negeri ajaib dari file BMP berarti bahwa data gambar disimpan dengan cara yang benar, yang pada gilirannya akan memungkinkan dekoder yang disederhanakan.

Dekoder

Tidak ada trik khusus dalam dekoder. Palet diisi oleh encoder, dan ditampilkan di sini dengan nilai-nilai dummy. Ini bisa menjadi sedikit lebih pendek jika tidak kembali ke DOS saat tombol ditekan, tetapi tidak mengasyikkan pengujian tanpa itu. Jika Anda merasa harus melakukannya, Anda dapat mengganti tiga instruksi terakhir dengan jmp $menyimpan beberapa byte. (Jangan lupa untuk memperbarui header file jika Anda melakukannya!)

BMP menyimpan palet sebagai BGR ( bukan RGB) kembar tiga, diisi dengan nol. Ini membuat pengaturan palet VGA lebih mengganggu dari biasanya. Fakta bahwa BMP disimpan terbalik hanya menambah rasa (dan ukuran).

Tercantum di sini dalam gaya 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:
gastropner
sumber
Bagus. Saya juga memikirkan pasangan BMP / MS-DOS COM; Saya akan menerapkannya jika tidak ada jawaban dalam satu minggu. Namun, saya akan membutuhkan lebih dari 10K: Karena saya tidak menganggap bahwa register diinisialisasi nol, saya akan menempatkan instruksi lompatan pada file offset 2. Dan karena bidang ini ditafsirkan sebagai "ukuran file" dalam file BMP, Saya harus mengisi file BMP dengan byte "dummy" untuk memastikan bidang "ukuran file" mewakili ukuran file yang benar.
Martin Rosenau
@MartinRosenau Aku benar-benar harus tidak menganggap beberapa nilai register yang biasanya saya lakukan (sesuai fysnet.net/yourhelp.htm ), karena header mengkritik register, dan bahkan byte pertama dari PSP, necessating int 0x20lebih ret.
gastropner