Виведіть твердий 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 байт Метод переплетення: 1 байт
технозавр

@technosaurus ... гм, що?
Дверна ручка

1
Ваш приклад невірний: бітова глибина: 8 (RRGGBBAA). Бітова глибина 8 є (RGBA) не (RRGGBBAA).
Гленн Рендерс-Персон

Відповіді:


6

Перл, 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; для стиснення даних зображення.

Обидві бібліотеки не пов'язані із зображеннями.

Безголівки:

# 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за замовчуванням. Функція не потребує посилань як аргументів, а також повертає результат безпосередньо. Це дозволяє позбутися змінної $o.

Дякую за відповідь Майкла :

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

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

Дякуємо за коментар VadimR з великою кількістю порад:

  • use String::CRC32;коротше, ніж use Digest::CRC crc32;.
  • y///c-4коротше, ніж -4+y///c.
  • Рядок сканування тепер побудований за шаблоном CH*із повторенням у значенні.
  • Видалення $iза допомогою значення посилання.
  • Голі слова замість рядків для типів шматка.
  • Тепер параметри зчитуються шляхом зіставлення вхідного рядка (опції -p) STDIN з відповідним роздільником пробілу / /. Тоді перший варіант входить, $`а другий аргумент входить $'.
  • Опція -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а / /;аргументи розміщуються в передматчі та постматчі. Цікаво, чи я щось пропустив, і оцінка може
опуститись

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, тому якщо він працює для всіх інших, це я роблю неправильно.
користувач2846289

1
@VadimR, так 214, ти маєш рацію. Я перевірив, створене зображення для мене дійсне.
Майкл М.

Для мене (я тестую з PHP 5.4.27.0), зображення коротке на 4 байти - чи не слід Adler-32 додавати до спущених даних? IE та Chrome із задоволенням відображають зображення, як є, FF - ні. Різні додатки теж поводяться по-різному з цим зображенням.
користувач2846289

4

Пітон, 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.