Python PNG画像を自力で出力する その4



1から3までのことを踏まえて、ざっくりプログラムしてみました。


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import binascii,struct,zlib
from struct import *

class MyPNG:
  """
  初期化 イメージの大きさをピクセル単位で指定する
  """
  def __init__(self, width, height):
    self.width = width
    self.height = height
    self.pix = []
    #内包するピクセルを1(白)で初期化
    for h in range(height):
      self.pix.append([1]*width)
    
  """
  長方形を描画する
  例)
  rectangle((10,10,100,100))
  """
  def rectangle(self, xy):
    x1,y1,x2,y2 = xy
    
    #todo 引数チェック処理
    
    #塗りつぶし実行
    for y in range(y1, y2):
      for x in range(x1, x2):
        self.pix[y][x] = 0
    
    
  """
  イメージの保存を実行する
  """
  def save(self, filepath):
    self.open(filepath)
    
    #pngヘッダ出力
    self.write_png_header()
    #ihdrチャンク出力
    self.write_ihdr_chunk()
    #idatチャンク出力
    self.write_idat_chunk()
    #iendチャンク出力
    self.write_iend_chunk()
    
    self.close()
    
  
  def open(self, filepath):
    self.f = open(filepath, 'wb')
  
  def close(self):
    self.f.close()
  
  #pngヘッダー出力
  def write_png_header(self):
    self.write((0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A))
  
  #ihdrチャンク出力
  def write_ihdr_chunk(self):
    #■長さ
    self.write((0x00, 0x00, 0x00, 0x0D))
    
    datas = []
    #■チャンク形式
    datas.extend((0x49, 0x48, 0x44, 0x52))
    #■幅
    datas.extend(unpack('BBBB', pack('>L', self.width)))
    #■高さ
    datas.extend(unpack('BBBB', pack('>L', self.height)))
    #■ビット深度、カラータイプ、圧縮方法、フィルター方法、インターレース方法
    datas.extend((0x01, 0x00, 0x00, 0x00, 0x00))
    #■CRC32
    datas.extend(self.crc32(datas))
    
    self.write(datas)
    
  #idatチャンク出力
  def write_idat_chunk(self):
    datas = []
    for y in range(self.height):
      line = ""
      datas.append(0)
      for x in range(self.width):
        line += str(self.pix[y][x])
        if len(line) == 8:
          datas.append(int(line, 2))
          line = ""
    
      if line != "":
        line += "0" * (8 - len(line))
        datas.append(int(line, 2))
    
    target = ""
    for d in datas:
      target += chr(d)
      
    #zip圧縮
    lines = zlib.compress(target)
    datas = []
    for l in lines:
      datas.append(ord(l))
    
    #■長さ
    self.write(unpack('BBBB', pack('>L', len(datas))))
    #■チャンク形式(IDAT)挿入 0x49 0x44 0x41 0x54
    datas.insert(0, 0x49)
    datas.insert(1, 0x44)
    datas.insert(2, 0x41)
    datas.insert(3, 0x54)
    
    #■CRC32
    datas.extend(self.crc32(datas))
    
    self.write(datas)
    
    
  #iendチャンク出力
  def write_iend_chunk(self):
    #■長さ
    self.write((0x00, 0x00, 0x00, 0x00))
    #■チャンク形式(IEND)
    self.write((0x49, 0x45, 0x4E, 0x44))
    #■CRC32
    self.write((0xAE, 0x42, 0x60, 0x82))
  
  #バイトデータ出力
  def write(self, values):
    for v in values:
      self.f.write(pack('B', v))
      
  #crc32を計算
  def crc32(self, datas):
    target = ""
    for d in datas:
      target += chr(d)
      
    crc = binascii.crc32(target)
    return unpack('BBBB', pack("!l", crc))

if __name__ == '__main__':
  png = MyPNG(width=200, height=50)
  png.rectangle((100,5,120,45))
  png.save('test.png')



ちゃんと出力されている模様です。



もどる