Вывести сплошной 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.