ออก PNG ที่เป็นของแข็งจากรอยขีดข่วน


11

อินพุต : สีฐานสิบหก RGBA c(เช่นFFFF00FF) และจำนวนเต็ม> 0 และ <1,000 n(เช่น200)

เอาท์พุท : วัตถุดิบไบต์ของไฟล์ PNG ดังกล่าวว่าเมื่อการส่งออกที่มีการบันทึกเป็นไฟล์และเปิดในโปรแกรมดูภาพการnโดยnภาพที่เต็มไปด้วยสีcจะปรากฏ

ข้อมูลจำเพาะ : โปรแกรมของคุณควรเอาท์พุทว่า :

  • ส่วนหัว PNG (เป็น89504E470D0A1A0Aเลขฐานสิบหก)
  • อันIHDRประกอบด้วยข้อมูลจำเพาะเหล่านี้:
    • ความกว้าง: อินพุตก่อนหน้า n
    • height: อินพุตก่อนหน้า n
    • ความลึกบิต: 8( RGBA)
    • ประเภทสี: 6(truecolor พร้อมอัลฟา)
    • วิธีการบีบอัด: 0
    • วิธีการกรอง: 0
    • วิธี interlace: 0
  • หนึ่งIDATชิ้นขึ้นไปที่มีข้อมูลภาพ (ภาพที่เป็นของแข็งของสีที่ป้อนก่อนหน้านี้c); อาจถูกบีบอัดหรือไม่บีบอัด
  • IENDก้อนท้ายภาพ

รายละเอียดเพิ่มเติมมีอยู่ในWikipedia , บนเว็บไซต์ W3หรือผ่านการค้นหาโดย Google

ข้อ จำกัด :

  • คุณไม่สามารถใช้ไลบรารีรูปภาพหรือฟังก์ชันที่ออกแบบมาเพื่อทำงานกับรูปภาพทุกชนิด
  • โปรแกรมของคุณต้องทำงานในเวลาไม่เกิน 3 นาทีและส่งออกไฟล์ที่ต่ำกว่า 10 MB สำหรับอินพุตทั้งหมด (การตรวจสอบสติ)
  • นี่คือดังนั้นโค้ดที่สั้นที่สุดเป็นไบต์จะเป็นผู้ชนะ!

คุณบอกว่าไฟล์อาจไม่ได้รับการบีบอัดทั้งหมด แต่จะต้องไม่เกิน 30kB สำหรับอินพุตทั้งหมด 999x999ไฟล์มีมากกว่า 30720 พิกเซลเพื่อให้ดูเหมือนว่าตนเองขัดแย้ง
ปีเตอร์เทย์เลอร์

@ PeterTaylor หืมมเหตุผลบางอย่างที่ฉันคิดว่า 30 KB นั้นมากเกินพอ ไม่ทราบว่าฉันคิดอะไรอยู่ ... แก้ไข (และฉันแค่บอกว่าคุณอาจใช้หรือไม่ใช้การบีบอัดก็ได้แล้วแต่ที่คุณต้องการ)
Doorknob

ความกว้าง: 4 ไบต์ความสูง: 4 ไบต์ความลึกบิต: 1 ไบต์ประเภทสี: วิธีการบีบอัด 1 ไบต์: วิธีการกรอง 1 ไบต์: 1 ไบต์วิธี Interlace: 1 ไบต์
technosaurus

@technosaurus ... อืมอะไรนะ?
Doorknob

1
ตัวอย่างของคุณไม่ถูกต้อง: ความลึกของบิต: 8 (RRGGBBAA) บิตความลึก 8 คือ (RGBA) ไม่ใช่ (RRGGBBAA)
Glenn Randers-Pehrson

คำตอบ:


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 MB)

โซลูชันใช้ไลบรารีสองรายการ:

  • 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ตามค่าเริ่มต้น ฟังก์ชั่นไม่จำเป็นต้องมีการอ้างอิงเป็นข้อโต้แย้งและส่งกลับผลลัพธ์โดยตรง ที่ช่วยให้สามารถกำจัดตัวแปร$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โดยใช้การอ้างอิงค่า
  • คำเปลือยแทนสตริงสำหรับประเภทก้อน
  • ตัวเลือกในขณะนี้อ่านโดยการจับคู่สายป้อนข้อมูล 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เป็น OK CH*แม่$iจะหายไป\cZ, barewords จะ OK, -pและ/ /;สถานที่การขัดแย้งเข้า prematch และ postmatch ฉันสงสัยว่าฉันพลาดบางสิ่งบางอย่างและคะแนนจะต่ำกว่า 200 :)
user2846289

1
@VadimR: ขอบคุณมากสำหรับคำแนะนำที่เป็นประโยชน์ ฉันสามารถเล่นกอล์ฟต่อไปได้โดยใช้use Compress::Zlib;และได้ 10% ต่ำกว่า 200
Heiko Oberdiek

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 คุณพูดถูก ฉันตรวจสอบแล้วรูปภาพที่สร้างขึ้นนั้นถูกต้องสำหรับฉัน
Michael M.

สำหรับฉัน (ฉันทดสอบด้วย 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.