byte[] src = new byte[1024],dst; for(int i = 0; i < src.Length; i++) { src[i] = (byte)(i % 256); } using (MemoryStream ms = new MemoryStream()) using (DeflateStream ds = new DeflateStream(ms, CompressionMode.Compress, true)) { ds.Write(src, 0, src.Length); ds.Dispose(); ms.Position = 0; dst = new byte[ms.Length]; ms.Read(dst, 0, dst.Length); } src = dst; using (MemoryStream msSrc = new MemoryStream()) using (MemoryStream msDst = new MemoryStream()) using (DeflateStream ds = new DeflateStream(msSrc, CompressionMode.Decompress)) { msSrc.Write(src, 0, src.Length); msSrc.Position = 0; ds.CopyTo(msDst); msDst.Position = 0; dst = new byte[msDst.Length]; msDst.Read(dst, 0, dst.Length); }
圧縮と展開、両方のソース。
圧縮はCompressionMode.Compressで行う。
DeflateStreamのコンストラクタで第3引数を省略するとfalseになるが、この場合は圧縮が終わった時点で第1引数に指定したStreamをCloseする。この動作は、FileStreamを使ったときには問題にならないが、MemoryStreamを使った場合は、あとから読めなくなる。なので、trueを指定してStreamがCloseされないようにする。
また、DeflateStreamは、StreamがCloseされた際に圧縮を行うらしい。ということで、DeflateStream.Dispose()で強制的に閉じておく。
そして、StreamのPositionはStreamの最後に移動しているので、あらかじめ0に戻しておく。
最後に、Stream.Lengthを使って圧縮後の容量を確認し、Stream.Readでメモリに戻す。
展開はCompressionMode.Decompressで行う。
この際は読み込みStreamは閉じられてもかまわないので、第3引数は省略可能。
まずはSrcStreamにデータをコピーする。DeflateStreamは入力StreamのPositionを戻さないので、自分でSrcStream.Position=0にしておく。
そしてDeflateStream.CopyToでDstStreamにデータを移動する。CopyToやReadでDeflateStreamを読み出した際に、SrcStreamからの展開を行うらしい。
またCopyToで読み出すと、Positionも移動するので、ここでも0にしておく。
最後に、DstStreamのLengthで容量を確認し、Readでメモリに戻す。
とりあえず、上記のサンプルデータだとちゃんと展開はできることを確認している。
最初、入力に乱数を使っていて、全く圧縮されなく悩んだ。Deflateはハフマン符号圧縮と違い、過去のメッセージと同じデータを探してくるので、乱数のような同じメッセージが複数回出てこないようなデータに対しては圧縮率が極めて低い。
今回のサンプルのような、同じメッセージが複数回出てくるようなデータに対しては、かなり圧縮率が良い。例えば、今回のサンプルデータは0x00から0xFFまでの256バイトのメッセージが4回出てくる。最初の1回は圧縮できないが、残りの3回は「nバイト前から長さlバイトと同じ」という情報があれば良いので、その分を圧縮できる。今回は1024バイトが280バイトまで圧縮された。
/*
そう考えると、PNGの圧縮効率ってあんまり高くない気がする。ピクセルデータを直接ハフマン符号圧縮したほうが効率良さそう。当時のコンピュータの処理能力とか、そういう制限だったんだろうなぁ。
PNGは浮動小数点演算とかいらない分、JPEGよりは組み込み向けといえるのか? どうだろう。JPEGはブロック単体で展開できるはずだけど、PNGを展開するには最大で圧縮窓全体をカバーするメモリ(=32KiB)が必要になりそう。PNGはフォーマットや圧縮方法にバリエーションが多いので、自前でライブラリ書くのは面倒だな。
最近扱った組み込みの画像を扱う処理だと、マイコン内の処理が8bitIndexedだったので、BMP8bppIndexed限定にした。コレなら読み込みプログラムも凄まじく簡単に書ける。JPEGだとマイコン内で減色アルゴリズムを走らせる必要がある。PNGは8bppIndexedも保存できるけど、大抵のドローソフトでは8bppPNGでは保存できない気がする。結局、リソースの制限された組み込み系では8bppIndexedBMPが楽だと思う。読み込み早いし。プログラム楽だし。大抵のドローソフトで保存できるし。
*/
0 件のコメント:
コメントを投稿