Самостійне відображення зображення [закрито]


11

Фон

Є саморозпаковуються .ZIPфайли. Зазвичай вони мають розширення .EXE(і виконавши файл, який вони будуть вилучені), але, перейменувавши їх .ZIP, ви можете відкрити файл за допомогою певного програмного забезпечення для вилучення ZIP.

(Це можливо тому, що для .EXEфайлів потрібен певний заголовок, але для .ZIPфайлів потрібен певний трейлер, тому можливо створити файл, який має і .EXEзаголовок, і .ZIPтрейлер.)

Ваше завдання:

Створіть програму, яка створює файли зображень "саморекламування":

  • Програма повинна сприймати зображення в розмірі 64х64 (як мінімум 4 кольори), а вихідний файл "комбінований"
  • Вихідний файл програми повинен бути розпізнаний як файл зображення звичайними переглядачами зображень
  • Під час відкриття вихідного файлу за допомогою переглядача зображень відображається вхідне зображення
  • Вихідний файл також визнається виконуваним файлом для будь-якої операційної системи чи типу комп'ютера

    (Якщо створено файл для нечастої операційної системи чи комп'ютера, було б непогано, якщо є емулятор ПК з відкритим кодом. Однак це не потрібно.)

  • Під час виконання вихідного файлу також повинно відображатися вхідне зображення
  • Цілком імовірно, що перейменування файлу (наприклад, від .PNGto to .COM) необхідно
  • Не потрібно, щоб програма та її вихідний файл працювали на одній ОС; наприклад, програма може бути програмою Windows та вихідними файлами, які можна виконати на Commodore C64.

Критерій виграшу

  • Програма , яка виробляє наімалейшій вихідний файл виграє
  • Якщо розмір вихідного файлу відрізняється залежно від вхідного зображення (наприклад, через те, що програма стискає зображення), можливий найбільший вихідний файл, створений програмою, що представляє зображення 64х64 з до 4 кольорами

Між іншим

Під час читання цього питання у StackOverflow у мене виникла ідея щодо наступної загадки програмування .


Я додав виграшні теги умови (виклик коду в поєднанні з метагольфом - найкоротший вихід). Що стосується вхідного зображення 64х64, чи є у вас приклади зображень? Також, чи саме зображення має бути однаковим при перегляді? Чи можуть відрізнятися вихідне зображення та вхідне зображення? Якщо говорити конкретніше: скажімо, ми додаємо якийсь код для .exeчастини виклику, і при перегляді його як a .pngє модифіковані пікселі на основі цього .exeкоду. Чи дозволено це до тих пір, поки це ще .pngми можемо переглянути? Чи має також вихідне зображення мати принаймні 4 кольори?
Кевін Кройсейсен

2
Як ви визначаєте "загальний переглядач зображень"? Наприклад, чи зараховується Інтернет-браузер з HTML "кодом"?
Джо Кінг

@KevinCruijssen При інтерпретації як файл зображення вихідний файл повинен представляти те саме зображення, що і вхідний файл: однакова ширина і висота в пікселях, і кожен піксель повинен мати однаковий колір. Якщо формати файлів не підтримують абсолютно однакову палітру кольорів, кольори кожного пікселя повинні бути максимально наближеними. Те саме стосується файлу, інтерпретованого як виконуваний файл. Якщо вихідний файл являє собою програму "повний екран", він може відображати зображення де-небудь на екрані (в центрі, лівий верхній край, ...) або розтягувати його на весь розмір екрана.
Мартін Розенау

1
@JoKing "Розпізнається загальним переглядачем зображень" означає, що формат файлу може читати більшість комп'ютерів із заздалегідь встановленим програмним забезпеченням (наприклад, HTML), або багато користувачів завантажують безкоштовний інструмент для перегляду файлу ( наприклад, PDF). Я б сказав, що HTML + JavaScript можна розглядати як код, однак "переглядач зображень" не повинен виконувати код! Тож було б дозволено сказати, що веб-браузер - це "переглядач зображень", але в цьому випадку HTML не є "кодом". Або ви можете сказати, що HTML + JS - це "код", але в цьому випадку веб-браузер не є "переглядачем зображень".
Мартін Розенау

2
Сумно бачити таке цікаве питання закритим. Наскільки я розумію, будь-які занепокоєння повинні бути вирішені перед повторним відкриттям питання. Головне в коментарях - термін "загальний переглядач зображень", який є достатньо туманним, щоб бути неоднозначним, а зображення, яке відображається у стані (відповідно до занепокоєння @ KevinCruijssen), незмінним за наявністю виконуваного коду, варте уточнення. . Чи буде достатньо редагування, яке вирішує ці проблеми? (Зізнаюсь, не розумію неоднозначності "це чотири кольори чотирьох кольорів".)
gastropner

Відповіді:


5

8086 MS-DOS .COM файл / BMP, розмір вихідного файлу = 2192 байт

Енкодер

Кодер написано в C. Це потребує двох аргументів: вхідний файл і вихідний файл. Вхідний файл - це зображення формату RGB розміром 64x64 (це означає, що це просто 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;
}

Вихідний файл

Вихідний файл - це BMP-файл, який можна перейменувати .COM і запустити в середовищі DOS. Після її виконання він перейде в режим відеозйомки 13h та відобразить зображення.

Файл BMP має перший заголовок BITMAPFILEHEADER, який містить серед іншого поле ImageOffset, яке позначає, звідки у файлі починаються дані зображення. Після цього надходить BITMAPINFOHEADER з різною інформацією про декодування / кодування з наступною палітрою, якщо така використовується. ImageOffset може мати значення, яке вказує за межі кінця будь-яких заголовків, що дозволяє нам зробити пробіл для декодера для проживання. Приблизно:

BITMAPFILEHEADER
BITMAPINFOHEADER
PALETTE
<gap>
IMAGE DATA

Ще одна проблема - ввести декодер. BITMAPFILEHEADER та BITMAPINFOHEADER можна зафіксувати, щоб переконатися, що вони є юридичним машинним кодом (який не створює стан, який не підлягає відновленню), але палітра є складнішою. Ми, звичайно, могли зробити палітру штучно довшою і помістили машинний код туди, але я вирішив замість цього використовувати поля biXPelsPerMeter та biYPelsPerMeter, перші для правильного вирівнювання коду, а другі для стрибка в декодер. Ці поля, звичайно, тоді містять сміття в них, але будь-який переглядач зображень, який я протестував, показує зображення добре. Однак друк може призвести до особливих результатів.

Це, наскільки я знаю, відповідає стандартам.

Можна створити коротший файл, якби JMPінструкція була розміщена в одному із зарезервованих полів BITMAPFILEHEADER. Це дозволило б нам зберегти висоту зображення як -64 замість 64, що в магічній країні чудес файлів BMP означає, що дані зображення зберігаються правильним шляхом, що, в свою чергу, дозволить спростити декодер.

Дешифратор

Ніяких особливих хитрощів у декодері. Палітра заповнюється кодером і тут показана фіктивними значеннями. Це може бути трохи коротше, якби він не повернувся до DOS після натискання клавіші, але без цього не було забавного тестування. Якщо вам здається, що ви повинні, ви можете замінити останні три інструкції, 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; Я би реалізував це, якби відповідей протягом тижня не було. Однак мені знадобилося б набагато більше 10 К: Оскільки я не вважав, що регістри ініціалізовані нулем, я б розмістив інструкцію про перехід у зміщення файлу 2. І тому, що це поле трактується як "розмір файлу" у файлах BMP, Мені доведеться заповнити файл BMP "манекенними" байтами, щоб переконатися, що поле "розмір файлу" відображає правильний розмір файлу.
Мартін Розенау

@MartinRosenau я на самому справі був НЕ припустити , деякі із значень регістрів , які я зазвичай роблю (по fysnet.net/yourhelp.htm ), так як заголовки затирати регістри, і навіть перший байт PSP, necessating int 0x20більш ret.
гастропнер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.