Вывести сплошной PNG с нуля


11

Входные данные : шестнадцатеричный цвет RGBA c( FFFF00FFпример) и целое число> 0 и <1000 n( пример 200).

Вывод : Сырое байт файла PNG таким образом, что , когда выход сохраняется в файл и открыть в программе просмотра изображений, nпо nизображению , заполненного цветом cотображается.

Спецификация : Ваша программа должна выводить точно :

  • заголовок PNG ( 89504E470D0A1A0Aв шестнадцатеричном формате )
  • IHDRфрагмент , содержащий эти спецификации:
    • ширина: предыдущий ввод n
    • высота: предыдущий ввод n
    • битовая глубина: 8( RGBA)
    • тип цвета: 6(истинный цвет с альфа)
    • метод сжатия: 0
    • метод фильтра: 0
    • метод чересстрочной развертки: 0
  • один или несколько IDATфрагментов, содержащих данные изображения (сплошное изображение ранее введенного цвета c); может быть сжатым или несжатым
  • IENDконечного изображения кусок

Более подробная информация доступна в Википедии , на сайте W3 или через поиск Google.

Ограничения :

  • Вы не можете использовать любые библиотеки изображений или функции, предназначенные для работы с изображениями любого рода.
  • Ваша программа должна работать менее чем за 3 минуты и вывести файл размером менее 10 МБ для всех входных данных (проверка работоспособности).
  • Это , поэтому выиграет самый короткий код в байтах!

Вы говорите, что файл может быть полностью несжатым, но тогда он должен быть меньше 30 КБ для всех входных данных. 999x999Файл имеет более чем 30720 пикселей, так что кажется внутренне противоречивым.
Питер Тейлор

@PeterTaylor Хм, почему-то я думал, что 30 КБ было более чем достаточно. Не знаю, о чем я думал ... отредактировано. (И я только сказал, что вы можете или не можете использовать сжатие; в зависимости от того, что вы хотите.)
Дверная ручка

Ширина: 4 байта Высота: 4 байта Глубина в битах: 1 байт Тип цвета: 1 байт Метод сжатия: 1 байт Метод фильтра: 1 байт Метод чересстрочной
развертки

@technosaurus ... ну что?
Дверная ручка

1
Ваш пример неверен: разрядность: 8 (RRGGBBAA). Глубина в 8 бит (RGBA) отсутствует (RRGGBBAA).
Гленн Рандерс-Персон

Ответы:


6

Perl, 181

/ /;use String::CRC32;use Compress::Zlib;sub k{$_=pop;pack'Na*N',y///c-4,$_,crc32$_}$_="\x89PNG\r\n\cZ\n".k(IHDR.pack NNCV,$',$',8,6).k(IDAT.compress pack('CH*',0,$`x$')x$').k IEND

Размер составляет 180 байт и -pтребуется опция (+1). Счет тогда 181.

Аргументы задаются через STDIN в строке, разделенной пробелом, цветом в виде шестнадцатеричного значения (16 символов) и количеством пикселей для ширины / высоты, например:

 echo "FFFF00FF 200" | perl -p solidpng.pl >yellow200.png

yellow200.png

Размер файла составляет 832 байта. Изображение максимального размера (n = 999) того же цвета имеет 6834 байта (намного меньше 10 МБ).

В решении используются две библиотеки:

  • use Digest::CRC crc32; для значений CRC32 на концах чанка.
  • use IO::Compress::Deflate deflate; сжать данные изображения.

Обе библиотеки не связаны с изображениями.

Ungolfed:

# Perl option "-p" adds the following around the program:
#     LINE:
#     while (<>) {
#         ... # the program goes here
#     } continue {
#         print or die "-p destination: $!\n";

/ /;    # match the separator of the arguments in the input line
        # first argument, color in hex:  $`
        # second argument, width/height: $'                              #'

# load the libraries for the CRC32 fields and the data compression
use String::CRC32;
use Compress::Zlib;

# function that generates a PNG chunk:
#   N (4 bytes, big-endian: data length
#   N:                      chunk type
#   a* (binary data):       data
#   N:                      CRC32 of chunk type and data
sub k {
    $_ = pop; # chunk data including chunk type and
              # excluding length and CRC32 fields
    pack 'Na*N',
        y///c - 4,   # chunk length                                      #/
                     # netto length without length, type, and CRC32 fields
        $_,          # chunk type and data
        crc32($_)    # checksum field
}

$_ =                      # $_ is printed by option "-p".
    "\x89PNG\r\n\cZ\n"    # PNG header
        # IHDR chunk: image header with
        #   width, height,
        #   bit depth (8), color type (6),
        #   compresson method (0), filter method (0), interlace method (0)
    . k('IHDR' . pack NNCV, $', $', 8, 6)
        # IDAT chunk: image data
    . k('IDAT' .
          compress        # compress/deflate data
          pack('CH*',     # scan line with filter byte
              0,          # filter byte: None
              ($` x $')   # pixel data for one scan line                 #'`
          ) x $'          # n lines                                      #'
      )
        # IHDR chunk: image end
    . k('IEND');

Правки

  • use IO::Compress::Deflate':all';заменяется на use Compress::Zlib;. Последний compressпо умолчанию экспортирует функцию deflate . Функция не нуждается в ссылках в качестве аргументов, а также возвращает результат напрямую. Это позволяет избавиться от переменной $o.

Спасибо за ответ Майкла :

  • Функция k: вызов packможет быть удален с помощью шаблона Na*Nдля первого packв функции.

  • packШаблон NNCVс четырьмя значениями оптимизируется NNC3nс шестью значениями.

Спасибо за комментарий ВадимР с большим количеством советов:

  • use String::CRC32;короче чем use Digest::CRC crc32;.
  • y///c-4короче чем -4+y///c.
  • Строка сканирования теперь строится по шаблону CH*с повторением в значении.
  • Удаление $iс помощью ссылки на значение.
  • Голые слова вместо строк для типов чанков.
  • Опции теперь читаются путем сопоставления входной строки STDIN (опция -p) с соответствующим разделителем пробела / /. Тогда первый вариант в, $`а второй аргумент входит в $'.
  • Вариант -pтакже автоматически печатает $_.
  • "\cZ"короче чем "\x1a".

Лучшее сжатие

За счет размера кода данные изображения могут быть дополнительно сжаты, если применяется фильтрация.

  • Нефильтрованный размер файла для FFFF0FF 200: 832 байта

  • Фильтр Sub(горизонтальные различия пикселей): 560 байт

    $i = (                            # scan line:
             "\1"                     # filter "Sub"
             . pack('H*',$c)          # first pixel in scan line
             . ("\0" x (4 * $n - 4))  # fill rest of line with zeros
          ) x $n;                     # $n scan lines
  • Фильтр Subдля первой и Upостальных строк: 590 байт

    $i = # first scan line
         "\1"                     # filter "Sub"
         . pack('H*',$c)          # first pixel in scan line
         . ("\0" x (4 * $n - 4))  # fill rest of line with zeros
         # remaining scan lines 
         . (
               "\2"               # filter "Up"  
               . "\0" x (4 * $n)  # fill rest of line with zeros
           ) x ($n - 1);
  • Сначала нефильтрованная строка, затем фильтр Up: 586 байт

    $i = # first scan line
         pack('H*', ("00" . ($c x $n)))  # scan line with filter byte: none
         # remaining scan lines 
         . (
               "\2"               # filter "Up"
               . "\0" x (4 * $n)  # fill rest of line with zeros
           ) x ($n - 1);
    
  • Также Compress::Zlibможно настроить; самый высокий уровень сжатия может быть установлен с помощью дополнительной опции для уровня сжатия в функции compressза счет двух байтов:

    compress ..., 9;

    Размер файла примера yellow200.pngбез фильтрации уменьшается с 832 байт до 472 байт. Применительно к примеру с Subфильтром размер файла уменьшается с 560 байт до 445 байт ( pngcrush -bruteдальнейшее сжатие невозможно).


Отличный ответ (как всегда), но игра в гольф может пойти дальше - я получаю 202, +1 за -p. В дополнение к пониманию ответа Майкла ( NA*Nи NNCVшаблонов), - String::CRC32экспорт по умолчанию, y///c-4все в порядке, CH*шаблон, $iнет \cZ, голые слова в порядке, -pи / /;помещает аргументы в prematch и postmatch. Интересно, если я что-то пропустил и счет может стать ниже 200 :)
user2846289

1
@VadimR: Большое спасибо за полезные советы. Я мог даже use Compress::Zlib;
играть в

5

PHP 214

Я не специалист по PHP, здесь есть место для игры в гольф. Советы приветствуются.

<?function c($d){echo pack("Na*N",strlen($d)-4,$d,crc32($d));}echo"\x89PNG\r\n\x1a\n";c("IHDR".pack("NNCV",$n=$argv[1],$n,8,6));c("IDATx^".gzdeflate(str_repeat("\0".str_repeat(hex2bin($argv[2]),$n),$n)));c("IEND");

Создайте файл PNG:

php png.php 20 FFFF00FF > output.png

Генерация потока base64 (вставьте результат в адресную строку браузера)

echo "data:image/png;base64,`php png.php 200 0000FFFF | base64`"

Безголовая версия:

<?php 

//function used to create a PNG chunck
function chunck($data) {
  return pack("Na*N", //write a big-endian integer, a string and another integer
    strlen($data)-4,     //size of data minus the 4 char of the type
    $data,               //data
    crc32($data));       //compute CRC of data
}

//png header
echo "\x89PNG\r\n\x1a\n"; 

//IHDR chunck
echo chunck("IHDR".pack("NNCV", //2 big-endian integer, a single byte and a little-endian integer
                   $n=$argv[1], $n,
                   8, 6)); //6 also write 3 zeros (little endian integer)

//IDAT chunck
//create a binary string of the raw image, each line begin with 0 (none filter)
$d = str_repeat("\0".str_repeat(hex2bin($argv[2]),$n),$n);
echo chunck("IDATx^".
       gzdeflate($d)); //compress raw data

//IEND chunck
echo chunck("IEND");

Сейчас 214, не так ли? И я не могу получить правильное изображение как для игры в гольф, так и для игры в гольф, но у меня нет опыта работы с PHP, поэтому, если он работает для всех остальных, я ошибаюсь.
user2846289

1
@VadimR, да 214, ты прав. Я проверил, сгенерированное изображение действительно для меня.
Майкл М.

Для меня (я тестирую с PHP 5.4.27.0), изображение имеет длину 4 байта - не следует ли добавлять adler-32 к дефлированным данным? IE и Chrome рады отображать изображение как есть, FF - нет. Различные приложения ведут себя по-разному, также с этим изображением.
user2846289

4

Python, 252 байта

import struct,sys,zlib as Z
P=struct.pack
A=sys.argv
I=lambda i:P(">I",i)
K=lambda d:I(len(d)-4)+d+I(Z.crc32(d)&(2<<31)-1)
j=int(A[2])
print "\x89PNG\r\n\x1A\n"+K("IHDR"+P(">IIBI",j,j,8,6<<24))+K("IDAT"+Z.compress(("\0"+I(int(A[1],16))*j)*j))+K("IEND")

Этот скрипт принимает входные данные из argv. Запустите этот скрипт из командной строки, какpython 27086.py deadbeef 999

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.