さて、今回は「PNG」について。OS Xではスクリーンショットに使用されるなど、事実上システム標準の画像フォーマットとしての地位にあるが、歴史的経緯を知らず漫然と利用しているユーザも多そうだ。

PNGの種類

かつてOS標準のラスター画像フォーマットといえば、MacはPICTでWindowsはBMPだったが、現在では"主役級"とは言えない。写真はJPEG、WEBページのボタンにはPNGといった具合に、OSとは関係なく用途別に使い分けるほうが多いからだ。

特にPNGは高い圧縮率のほか、最大48bitのフルカラー(約280兆色)をサポート、複数の透過色を指定できる、という特徴から汎用的なラスター画像フォーマットとして普及している。特許問題を抱え扱いにくかったGIFの代替フォーマットとして登場した経緯もあり、基本的にはWEB素材やアイコンといった用途に利用されるが、フルカラー表示が可能なため画質劣化を避けたい画像に好適だ。

そんなPNGには、いくつかの種類がある。拡張子は同じ「.PNG」だが、扱える色数の違いで「PNG-8」(最大256色)と「PNG-24」(1678万色)、「PNG-32」(280兆色)に分類できるのだ。実写画像で使用した場合、JPEGよりファイルサイズが肥大化しがちだが、スクリーンショットのようなビットマップ画像はDeflateの高い圧縮率により、他フォーマットに比べファイルサイズで有利になる。

PNGを抜きに語れないのがiOSアプリだ。アイコンやスプラッシュ画像は必ずPNG、標準と高精細(Retina)それぞれの解像度で収録されていなければならない。OS Xでもスクリーンショットのフォーマットに採用されるなど、なくてはならない存在となっているが、依存度という点ではiOSのほうが上だ。PNGの"よしあし"がアプリの完成度に影響する可能性を考慮すると、単なる画像フォーマットとしては片付けられない存在といえるだろう。

Finder上では区別されないが、カラーのPNGファイルは「PNG-8」と「PNG-24」、「PNG-32」の3種に分類できる

PNGとJPEG、GIFの比較
PNG JPEG GIF(GIF89a)
ビット深度 8/16/24/48 24 8
色数 256/1678万/280兆 1678万 256
圧縮 可逆圧縮 非可逆圧縮 可逆圧縮
透過処理 ×
アニメーション × ×
インターレース
主な用途 ラスター画像全般 写真 色数を必要としない透過画像やアニメーション

PNGの見分け方

上述のとおり、扱える色数などの違いで分類されるPNGフォーマットだが、Finderで見るかぎり区別できない。PNG-8なのかPNG-24なのか、はたまたPNG-32なのかは、ファイルサイズからある程度は推測できるが、実際のところしっかり判断できる機能を備えた画像編集ソフトは意外に少ない。

「file」や「sips」コマンドでPNGファイルの詳細情報を調べることもできるが、わかるのはビット深度など一部に過ぎず、種類を判定するには足りない。PNGファイルを見分けるには、先頭部分にある画像として最低限必要な情報(チャンク)を分析し、カラータイプの種類とαチャンネルを判定しなければならないのだ。

$ file shot.png
shot.png: PNG image data, 570 x 366, 8-bit/color RGBA, non-interlaced

$ sips --getProperty bitsPerSample IMG_9917.png 
/Users/shinobu/Desktop/IMG_9917.png
bitsPerSample: 16

「プレビュー」で表示したPNGファイルの情報画面。この情報だけでは、PNG-8(左)とPNG-24(右)を見分けることはできない

多機能な画像編集ソフトを利用すればPNGファイルの種類を判定することは可能だが、なにぶん当コラムのこと、できればOS標準の機能で賄いたい。そこで用意したのが、リスト1に挙げるRubyスクリプト。『テキストエディット』などのテキストエディタにコピー&ペーストし、エンコーディング形式をUTF-8として保存してほしい。「pnginfo.rb」として保存した場合には、以下の要領でPNGファイルを指定すれば、判定に必要な情報が出力されるはずだ。

前述したとおり、PNGファイルを見分けるポイントはカラータイプとαチャンネルの2つ。カラータイプ「3」がPNG-8、「2」がPNG-24、「6」がPNG-32だ。リスト1ではαチャンネルの有無だけを判定しているため、PNG-8の透過の種類(インデックス透明カラーまたはαチャンネル)はわからないが、これでとりあえずの目的は達成できる。

なお、OS Xのスクリーンショットは「PNG-32」であり、ファイルサイズを小さくする余地はありそう。興味があれば、「pngquant]などの最適化ユーティリティを利用してみよう。

$ ruby pnginfo.rb png32.png

PNGファイルのカラータイプとαチャンネルの有無を検証するスクリプトを実行したところ。手もとのPNGファイルで試してみよう

リスト1(エンコーディング形式をUTF-8で保存すること)

# -*- coding: utf-8 -*-
require "stringio"
pngname = ARGF.filename
image = open(pngname, "rb")

sign   = image.read(8)
length = image.read(4).unpack(">N")[0]
name  = image.read(4)
data  = image.read(length)

ihdr = StringIO.new(data)
width       = ihdr.read(4).unpack(">N")[0]
height      = ihdr.read(4).unpack(">N")[0]
bit_depth   = ihdr.read(1).unpack("h")[0].to_i
color_type  = ihdr.read(1).unpack("h")[0].to_i
alpha = `/usr/bin/sips --getProperty hasAlpha '#{pngname}' | sed -n '2,2p'`

puts "横幅          %4d" % width
puts "高さ          %4d" % height
puts "ビット深度    %4d" % bit_depth
puts "カラータイプ  %4d" % color_type
puts "αチャンネル  %4s" % alpha