自己表示画像[終了]


11

バックグラウンド

自己解凍.ZIPファイルがあります。通常は拡張子.EXEが付きます(抽出されるファイルを実行することにより)が、名前をに変更すると.ZIP、いくつかのZIP解凍ソフトウェアでファイルを開くことができます。

.EXEファイルには特定のヘッダーが.ZIP必要ですが、ファイルには特定のトレーラーが必要であるため、これは可能です。.EXEヘッダーと.ZIPトレーラーの両方を持つファイルを作成することは可能です。)

あなたのタスク:

「自己表示」イメージファイルを作成するプログラムを作成します。

  • プログラムは、64x64の画像(少なくとも4色がサポートされている)を入力として受け取り、「結合された」ファイルを出力として受け取ります。
  • プログラムの出力ファイルは、一般的な画像ビューアによって画像ファイルとして認識されます。
  • 画像ビューアで出力ファイルを開くと、入力画像が表示されます
  • 出力ファイルは、任意のオペレーティングシステムまたはコンピュータータイプの実行可能ファイルとしても認識されます。

    (一般的でないオペレーティングシステムまたはコンピューター用のファイルが作成された場合、オープンソースのPCエミュレーターが存在していると便利ですが、これは必須ではありません。)

  • 出力ファイルを実行すると、入力画像も表示されます
  • ファイルの名前を変更する必要があります(たとえば、.PNGから.COM
  • プログラムとその出力ファイルを同じOSで実行する必要はありません。このプログラムは、たとえば、Windowsプログラムと、Commodore C64で実行できる出力ファイルです。

受賞基準

  • 生成するプログラムの最小出力ファイル勝利を
  • 出力ファイルのサイズが入力画像によって異なる場合(たとえば、プログラムが画像を圧縮するため)、最大 4色の64x64画像を表すプログラムによって作成された最大の出力ファイル数

ところで

StackOverflowでこの質問を読んだとき、私は次のプログラミングパズルのアイデアを思いつきました


落札条件タグを追加しました(メタゴルフと組み合わせたコードチャレンジ-最短の出力)。入力64x64画像について、いくつかのサンプル画像がありますか?また、画像自体は表示時に同じでなければなりませんか?または、出力画像と入力画像が異なることがありますか?より具体的に.exeは、チャレンジの一部にある種のコードを追加し、それをaとして表示すると、.pngこの.exeコードに基づいて変更されたピクセルがあるとしましょう。それが.png私たちが見ることができる限り、これは許可されますか?出力画像も4色以上である必要がありますか?
Kevin Cruijssen

2
「共通画像ビューア」をどのように定義しますか?たとえば、HTML「コード」を備えたインターネットブラウザはカウントされますか?
Jo King

@KevinCruijssen画像ファイルとして解釈される場合、出力ファイルは入力ファイルと同じ画像を表すものとします。同じ幅と高さのピクセル数で、各ピクセルの色は同じでなければなりません。ファイル形式がまったく同じカラーパレットをサポートしていない場合、各ピクセルの色はできるだけ近くなければなりません。実行可能ファイルとして解釈されるファイルについても同様です。出力ファイルが「全画面」プログラムを表す場合、画面上の任意の場所(中央、左上端など)に画像を表示したり、全画面サイズに拡大したりできます。
Martin Rosenau

1
@JoKing "一般的な画像ビューアで認識"は、ファイル形式がプリインストールされたソフトウェア(HTMLなど)を備えたほとんどのコンピュータで読み取れること、または多くのユーザーがファイルを表示するために無料のツールをダウンロードすることを意味します( PDFなど)。HTML + JavaScriptはコードと見なすことができますが、「画像ビューア」はコードを実行しないでください。したがって、Webブラウザーが「画像ビューアー」であると言うことは許可されますが、この場合、HTMLは「コード」ではありません。または、HTML + JSは「コード」であると言うことができますが、この場合、Webブラウザーは「画像ビューアー」ではありません。
Martin Rosenau

2
このような興味深い質問が終了したのは悲しいことです。私が理解している限り、質問を再開する前に懸念事項に対処する必要があります。コメントの主なものは、「一般的な画像ビューア」という用語で、あいまいになるほど十分にかすんでおり、実行可能コードの存在によって変更されていない状態(@KevinCruijssenの懸念による)で表示されている画像は、説明に値します。これらの懸念に対処する編集で十分でしょうか?(「4色4色」のあいまいさを理解していないことを認めます。)
ガストロッパー

回答:


5

8086 MS-DOS .COMファイル/ BMP、出力ファイルサイズ= 2192バイト

エンコーダー

エンコーダーはCで書かれています。入力ファイルと出力ファイルの2つの引数を取ります。入力ファイルは64x64のRAW RGBイメージです(つまり、単純に4096 RGBトリプレットです)。色の数は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の予約フィールドの1つに置かれた場合、ファイルを短くすることができます。これにより、画像の高さを64ではなく-64として保存できます。これは、BMPファイルの不思議な不思議の国では、画像データが正しい方法で保存されることを意味します。これにより、デコーダーが簡素化されます。

デコーダ

デコーダーに特別なトリックはありません。パレットにはエンコーダーが入力され、ここにダミー値が表示されます。キーを押してもDOSに戻らない場合は少し短くなる可能性がありますが、それがないとテストは面白くありませんでした。必要だと思われる場合は、最後の3つの命令をjmp $に置き換えて、数バイトを節約できます。(必要に応じて、ファイルヘッダーを更新することを忘れないでください!)

BMPは、ゼロで埋められたBGR(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のペアについても考えていました。1週間以内に回答がない場合は実装しました。ただし、10Kをはるかに超える容量が必要でした。レジスタがゼロで初期化されるとは想定していなかったため、ファイルオフセット2にジャンプ命令を配置しました。また、このフィールドはBMPファイルで「ファイルサイズ」として解釈されるため、 「ファイルサイズ」フィールドが正しいファイルサイズを表すように、BMPファイルを「ダミー」バイトで埋める必要があります。
Martin Rosenau

@MartinRosenau ヘッダークローバーが登録され、PSPの最初のバイトでさえも必要になるため、私は実際に(fysnet.net/yourhelp.htmに従って)通常行うレジスタ値の一部を想定する必要がありませんでした。int 0x20ret
ガストロパ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.