|
|
2006/9/18(Mon) 17:04:41|NO.2496
Dripです。
縦横1000ピクセルを超える巨大なビットマップを扱うプログラムを作っているのですが、
使用する色が2色なため、保存するときは2色ビットマップを用いることで
データサイズを256色の画像よりも約8倍軽くすることができるのですが、
HSPから2色ビットマップを書き出すことはできないでしょうか。
2色ビットマップはあまり使用されないのか、情報が少なく困っています。
大きな画像でも素早く2色ビットマップを書き出す
何か良い方法がありましたら教えていただければ幸いです。
|
|
2006/9/22(Fri) 20:04:10|NO.2557
> 大きな画像でも素早く2色ビットマップを書き出す
>何か良い方法がありましたら教えていただければ幸いです。
2色というのがモノクロ(白黒2色)限定であるなら "GetDIBits" 関数が簡単、
そうでないなら "CreateDIBSection" 関数を使うのが良いと思います。
下記サンプルの頭で定義している MONOCHROME_IMAGE という定数が
0 なら "CreateDIBSection" 関数で、
1 なら "GetDIBits" 関数で実行ファイルのあるディレクトリに
"1ビット.bmp" という名前のファイルを出力する(そのように作ったつもり)ので
試してみて下さい。
APIだから大きな画像でも処理速度は遅くはないと思いますが、
どちらも減色やビット変換は全てAPI任せなので画質の方は...(-_-;?
#define MONOCHROME_IMAGE 1; // [ 0 = "CreateDIBSection" / 1 = "GetDIBits" ]
#uselib "GDI32.DLL"
#if MONOCHROME_IMAGE
#func GetDIBits "GetDIBits" int, int, int, int, int, int, int
#else
#func CreateDIBSection "CreateDIBSection" int, int, int, int, int, int
#func CreateCompatibleDC "CreateCompatibleDC" int
#func SelectObject "SelectObject" int, int
#func BitBlt "BitBlt" int, int, int, int, int, int, int, int, int
#func DeleteDC "DeleteDC" int
#func DeleteObject "DeleteObject" int
#define SRCCOPY $00CC0020
#endif
#define DIB_RGB_COLORS $0000
#define BI_RGB $0000
dialog "jpg;*.bmp;*.gif", 16, "セーブ元画像の選択" : if ( stat == 0 ) : end
screen 0,,,,0,0 : picload refstr : width ginfo_dispx - 6
title " 1ビットでBMPセーブする元画像!"
biSizeImage = ( ginfo_sx * 1 + 31 ) / 32 * 4 * ginfo_sy
bfOffBits = 14 + 40 + 4 * 2; // sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * 2
bfSize = biSizeImage + bfOffBits
dim bmf, ( bfSize + 3 ) / 4
// BITMAPFILEHEADER
wpoke bmf, 0, 'M' << 8 | 'B'; // bfType
lpoke bmf, 2, bfSize
wpoke bmf, 6, 0; // bfReserved1
wpoke bmf, 8, 0; // bfReserved2
lpoke bmf, 10, bfOffBits
dupptr bmi, varptr(bmf) + 14, biSizeImage + 40 + 4 * 2, 4
// BITMAPINFOHEADER
bmi(0) = 40; // biSize = sizeof(BITMAPINFOHEADER)
bmi(1) = ginfo_sx; // biWidth
bmi(2) = ginfo_sy; // biHeight
bmi(3) = 1 << 16 | 1; // biBitCount << 16 | biPlanes
bmi(4) = BI_RGB; // biCompression
bmi(5) = biSizeImage
bmi(6) = 0; // biXPixPerMeter
bmi(7) = 0; // biYPixPerMeter
bmi(8) = 2; // biClrUsed
bmi(9) = 0; // biClrImporant
#if MONOCHROME_IMAGE
mref bmscr, 67
GetDIBits hdc, bmscr(7), 0, ginfo_sy, varptr(bmi) + 40 + 4 * 2, varptr(bmi), DIB_RGB_COLORS
#else
// RGBQUAD ( 下の2行で設定した2色の1ビットBMP画像が生成されます )
bmi(10) = 0x000000FF; // RGBQUAD[0]
bmi(11) = 0x0080FF80; // RGBQUAD[1]
CreateDIBSection 0, varptr(bmi), DIB_RGB_COLORS, varptr(pvBits), 0, 0 : hDIB = stat
CreateCompatibleDC hdc : hMDC = stat
SelectObject hMDC, hDIB : hOLD = stat
BitBlt hMDC, 0, 0, ginfo_sx, ginfo_sy, hdc, 0, 0, SRCCOPY
SelectObject hMDC, hOLD
DeleteDC hMDC
dupptr Bits, pvBits, biSizeImage, 4
memcpy bmi, Bits, biSizeImage, 40 + 4 * 2, 0
DeleteObject hDIB
#endif
bsave dir_exe +"\\1ビット.bmp", bmf, bfSize, -1
if ( strsize != bfSize ) : dialog "セーブ失敗!" : end
screen 1,,,0,22,22 : picload dir_exe +"\\1ビット.bmp"
title " セーブした1ビットBMP画像! ( ファイルサイズ = "+ bfSize +" bytes )"
| |
|
2006/9/23(Sat) 13:40:55|NO.2572
Dripです。
Bombさん、ご返信ありがとうございます。
なんとそんなやりかたがあったのですね;
2色ビットマップを書き出すソフトはWindowsPaintぐらいしか見かけないので、
WindowsPaintの2色書き出しはてっきりWindowsPaintの内部処理だと思ってました;
大変参考になるアドバイス有難うございました。
|
|
2006/9/26(Tue) 21:00:14|NO.2655
書くの忘れてたけど、"GetDIBits" 関数ならRLE(ランレングス)圧縮の
ビットマップファイルを作る事も可能なので、横方向に同じ色が連続して
いるような白黒2色画像なら1ビット無圧縮でセーブするより、
4ビットRLE圧縮でセーブした方がサイズを小さく出来る可能性が高いよ。
興味があれば、試行錯誤してみて下さい。
|
|
2006/10/1(Sun) 18:06:15|NO.2771
Dripです。
ご返信が遅れてすみません。解決マークをつけてしまっていたのでbombさんのご返信に
全く気づきませんでした。2度にわたる情報提供本当に有難うございます。
確かにランレングス圧縮を用いれば効率のよい保存ができそうですね。
今回の保存の仕方は初めてだったので、googleからGetDIBitsでBI_RLE4を指定して、
スクリーンの表示状態を圧縮する方法を探り実行してみたのですが、
どういうわけか破損したビットマップしか生成されず少し困ってます。
単純な画像なのにファイルサイズが非圧縮時よりかなり大きかったり、
Windowsペイントでしか開けなかったりと、なんだか挙動が怪しいです。
biCompressionにBI_RLE4($0002)を指定した場合は、
biBitCount=4、biClrUsed=16、等とRLE4用に値を差し替えていったのですが、
そもそも圧縮後のファイルサイズが不明なのでbiSizeImageの値も特定できず、
うまくいきません。
そう言えば参考にしたサイトの中で
RLE4とRLE8の値($0001と$0002)が逆に説明されているところもあったので、
なんだか参考元の情報も怪しいのかも・・。
( http://www.kk.iij4u.or.jp/~kondo/bmp/)
うぅん;
|
|
2006/10/1(Sun) 23:46:11|NO.2790
> ご返信が遅れてすみません。解決マークをつけてしまっていたのでbombさんのご返信に
>全く気づきませんでした。2度にわたる情報提供本当に有難うございます。
いえいえ、私も最初の返信から4日もたってから思い出しているので気にしないで下さい。
> 単純な画像なのにファイルサイズが非圧縮時よりかなり大きかったり、
>Windowsペイントでしか開けなかったりと、なんだか挙動が怪しいです。
RLE圧縮は、画像によっては無圧縮よりサイズが大きくなったりしますよ...(^_^;
>そもそも圧縮後のファイルサイズが不明なのでbiSizeImageの値も特定できず、
>うまくいきません。
BITMAPINFOHEADER構造体の biSize メンバから biCompression メンバまでに値を設定して
"GetDIBits" 関数の lpvBits を NULL(0) で実行すれば RGBQUAD部分にパレットデータと
biSizeImage メンバにビットマップデータのサイズが代入されて返るので、
その値でRLE圧縮時のデータサイズ(biSizeImage)を知る事が出来ます。
#define global RLE_SUPPORT 1; // RLE圧縮サポート = [ 0 = 無効 / 1 = 有効 ]
#module
#ifndef __GDI32__
#uselib "GDI32.DLL"
#func GetDIBits "GetDIBits" int, int, int, int, int, int, int
#endif
#define DIB_RGB_COLORS $0000
#define BI_RGB $0000
;#define BI_RLE8 $0001
;#define BI_RLE4 $0002
;=================================================================================
; ○ 指定ビット数のビットマップファイルセーブ!
;=================================================================================
#deffunc dibsave str fname, int bit
// bit = セーブするビットマップファイルのビット数( 1 / 4 / 8 / 16 / 24 / 32 bit )
// ( RLE_SUPPORT が ON(1) なら 4/8ビットは圧縮効果がある時はRLE圧縮形式でセーブします )
// 戻り値 = 成功なら BITMAPINFOHEADER->biCompression の値をかえす。負の値ならセーブエラー!
mref bmscr, 67
switch bit
case 1
case 4
case 8
case 16
case 24
case 32
biBitCount = bit
swbreak
default
biBitCount = 24 - bmscr(3) * 16
swbreak
swend
biSizeImage = ( ginfo_sx * biBitCount + 31 ) / 32 * 4 * ginfo_sy
biClrUsed = ( biBitCount <= 8 ) << biBitCount
bfOffBits = 14 + 40 + 4 * biClrUsed; // sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * biClrUsed
bfSize = biSizeImage + bfOffBits
dim bmf, ( bfSize + 3 ) / 4
// BITMAPFILEHEADER
wpoke bmf, 0, 'M' << 8 | 'B'; // bfType
lpoke bmf, 2, bfSize
wpoke bmf, 6, 0; // bfReserved1
wpoke bmf, 8, 0; // bfReserved2
lpoke bmf, 10, bfOffBits
dupptr bmi, varptr(bmf) + 14, biSizeImage + 40 + 4 * biClrUsed, 4
// BITMAPINFOHEADER
bmi(0) = 40; // biSize = sizeof(BITMAPINFOHEADER)
bmi(1) = ginfo_sx; // biWidth
bmi(2) = ginfo_sy; // biHeight
bmi(3) = biBitCount << 16 | 1; // biBitCount << 16 | biPlanes
bmi(4) = BI_RGB; // biCompression
bmi(5) = biSizeImage
bmi(6) = 0; // biXPixPerMeter
bmi(7) = 0; // biYPixPerMeter
bmi(8) = biClrUsed; // ← ここは 0 でも可!
bmi(9) = 0; // biClrImporant
#if RLE_SUPPORT
if ( biBitCount == 4 || biBitCount == 8 ) {
// 4ビット or 8ビットの時はRLE圧縮時のサイズを調べる!
bmi(4) = ( biBitCount == 4 ) * 2 + ( biBitCount == 8 ); // biCompression ( BI_RLE4 / BI_RLE8 )
GetDIBits hdc, bmscr(7), 0, ginfo_sy, 0, varptr(bmi), DIB_RGB_COLORS
if ( stat != 0 && bmi(5) < biSizeImage ) {
// 無圧縮よりもサイズが小さい時はRLE圧縮形式でセーブする!
bfSize = bmi(5) + bfOffBits
lpoke bmf, 2, bfSize
} else {
// メモリの再確保が面倒なので圧縮効果がない時は無圧縮BMP形式でセーブする!
bmi(4) = BI_RGB
bmi(5) = biSizeImage
}
}
#endif
GetDIBits hdc, bmscr(7), 0, ginfo_sy, varptr(bmi) + 40 + 4 * biClrUsed, varptr(bmi), DIB_RGB_COLORS
if ( stat != ginfo_sy ) : return -1
bsave fname, bmf, bfSize, -1 : return ( ( strsize == bfSize ) * ( bmi(4) + 1 ) - 1 )
;-----------------------------------------------------------------------------------
#global
;===================================================================================
; [ TEST SAMPLE ]
dialog "jpg;*.bmp;*.gif", 16 : if ( stat == 0 ) : end
screen 0,,,,0,0 : picload refstr : width ginfo_dispx - 6 : title " セーブ元画像!"
dibsave dir_exe +"\\test.bmp", 1; // 1ビットでBMPセーブ!
if ( stat < 0 ) : dialog "1ビットセーブ失敗!" : end; // -1 ならセーブエラー!
screen 1,,,,22,22 : picload dir_exe +"\\test.bmp"; // セーブした1ビットBMPをロード!
exist dir_exe +"\\test.bmp"
title " 1ビットでセーブしたBMP画像 ( ファイルサイズ = "+ strsize +" bytes )"
dibsave dir_exe +"\\test.bmp", 4; // 1ビットでセーブした白黒画像を4ビットRLE圧縮形式で上書きセーブ!
biCompression = stat; // biCompression の値が返る!
if ( biCompression < 0 ) : dialog "4ビットセーブ失敗!" : end; // -1 ならセーブエラー!
screen 2,,,,44,44 : picload dir_exe +"\\test.bmp"; // セーブした4ビットBMPをロード!
exist dir_exe +"\\test.bmp"
if ( biCompression ) {
title " RLE圧縮形式でセーブしたBMP画像 ( ファイルサイズ = "+ strsize +" bytes )"
} else {// biCompression == 0 なら圧縮効果がないので無圧縮でセーブされています!
title " 無圧縮でセーブしたBMP画像 ( ファイルサイズ = "+ strsize +" bytes )"
}
# サンプル書いてて気付いたけどHSP3の picload は 16ビットやトップダウンの
# ビットマップファイルも読めるようになってますね。(*^o^*)V
# ヘルプは、相変わらず 4,8,24bitのデータってなったままですけど...
| |
|
2006/10/2(Mon) 20:49:53|NO.2817
Dripです。
Bombさん、詳しい説明とサンプルスクリプトまでご教授くださり、
本当に有難うございます。今少し忙しくて詳しく見れないのですが、取り急ぎお礼まで。
スクリプトには、識別子のコメントまで詳しく書かれているので、大変参考になりそうです。
今回は本当に色々と勉強になりました。
Bombさん、度々のご丁寧なご返信に心から感謝致します。
|
|