RPGツクールXP/VX用のRGSS素材です。
一部英語版対応です。
(A few materials can be used for English RPG MAKER XP/VX.)
競合率はルファーの独断と偏見なのでたまにハズレます。
#==============================================================================
# ■ PNG 出力ライブラリ
#------------------------------------------------------------------------------
# ビットマップオブジェクトを PNG (*.png) ファイルに出力します。
# 対応RGSS : 1 2
# (C) ルファー
# WebSite https://tkoolerlufar.pages.dev/
#==============================================================================
=begin
=対応RGSS
1 2
=必要なライブラリ
ルファー作スクリプト素材共通ライブラリ
=他の素材との競合の可能性
... 考える必要ナシ
=RPGツクールXPでの導入方法
Main より上に新セクションを作成して、このプログラムをそこにコピーする
=使い方。
Bitmap クラスの「write_to_png_file」メソッドを呼び出すことで、
そのビットマップを png ファイルに出力します。
文法:
write_to_png_file(file[, show_progress])
* file : 出力先ファイルのパスまたは出力先 IO
* show_progress : 進歩状況を表示するかどうか (初期値 true)
show_progress を true にしても、メモリが無いと進行状況を表示しません。
なお、RPGツクールXPでは、この引数に false を与えると
ハングアップすることがあるので気をつけてください。
=出力する PNG について。
ビットマップオブジェクトの幅か高さのいずれかが
2,147,483,647 ピクセルを超えると出力できません。
ま、そんなビットマップ作らないと思うけど。
解像度は、はがきサイズ (横10cm X 縦14.1cm) に合わせます。
=参考サイト。ありがとうございます!
Sophia Web
http://homepage2.nifty.com/sophia0/
http://homepage2.nifty.com/sophia0/png.html
PNG (Portable Network Graphics) Specification, version 1.0
http://www.libpng.org/pub/png/spec/1.0/
RFC 1950, 1951
http://www.rfc-editor.org/rfc/rfc1950.txt
http://www.rfc-editor.org/rfc/rfc1951.txt
futomi's CGI Cafe
トップページ
http://www.futomi.com/
RFC 1950, 1951 の和訳 (参考用)
http://www.futomi.com/lecture/japanese/rfc1950.html
http://www.futomi.com/lecture/japanese/rfc1951.html
=更新履歴
2010/06/01 解像度が、はがきの背景サイズに合わせていたのを前景サイズに修正
その他の表記ミスも訂正
2010/04/13 公開
=新規定義しているメソッド
Bitmap#update_crc
Bitmap#crc
Bitmap#write_to_pngfile
=新規定義している定数
Bitmap::CRC_TABLE
=end
#----------------------------------------------------------------------------
# ◆ Bitmap クラス
#----------------------------------------------------------------------------
class Bitmap
#--------------------------------------------------------------------------
# ○ 定数
#--------------------------------------------------------------------------
CRC_TABLE = Array.new(0x100) do |n| # CRC 表
c = n
010 .times do |k|
if c & 1 != 0
c = 0xedb88320 ^ (c >> 1)
else
c = c >> 1
end
end
c
end.freeze
#--------------------------------------------------------------------------
# ○ CRC の更新
# crc : CRC
# buf : バッファ
#--------------------------------------------------------------------------
def update_crc(crc, buf)
c = crc
buf.length.times do |n|
c = CRC_TABLE[(c ^ buf[n]) & 0xff] ^ (c >> 010)
end
return c
end
#--------------------------------------------------------------------------
# ○ CRC の取得
# buf : バッファ
#--------------------------------------------------------------------------
def crc(buf)
return update_crc(0xffffffff, buf) ^ 0xffffffff
end
#--------------------------------------------------------------------------
# ○ PNG ファイルへの書き込み
# file : 出力先ファイルのパスまたは出力先 IO
# show_progress : 進歩状況を表示するかどうか (初期値 true)
# 戻り値 : 成功すれば偽。画像が大きすぎれば真。
#--------------------------------------------------------------------------
def write_to_pngfile(file, show_progress = true)
if width > 0x7fffffff or height > 0x7fffffff
raise(RuntimeError, "ビットマップが大きすぎます。", caller)
end
begin
# 「お待ちください」
if show_progress
begin
colorless = Color.new(0.0, 0.0, 0.0, 0.0)
sprite = Sprite.new
sprite.z = 077777
sprite.bitmap = Bitmap.new(screen_width, screen_height)
sprite.bitmap.fill_rect(0, 0, screen_width, screen_height,
colorless)
sprite.bitmap.draw_text(0, screen_height / 2 - 32, screen_width, 32,
"WRITING A BITMAP TO A PNG IMAGE. PLEASE WAIT....", 1)
sprite.bitmap.font.color = Color.new(128.0, 128.0, 128.0)
rescue RGSSError
raise if $!.message !~ /^failed to create /
show_progress = false
end
end
refresh_progress = Proc.new {|str|
next unless show_progress
sprite.bitmap.fill_rect(0, screen_height / 2, screen_width, 32,
colorless)
sprite.bitmap.draw_text(0, screen_height / 2, screen_width, 32, str)
}
# 何秒もビジー状態になると不安になってくるので
# 別スレッドで進歩状況を表示しながら処理
processing = true
thread_which_update_frame = Thread.start do
unless show_progress
Thread.stop
Thread.exit
end
frame_count = Graphics.frame_count
frame_rate = Graphics.frame_rate
Graphics.frame_count = 0
Graphics.frame_rate = 10
while processing
sprite.visible = Graphics.frame_count & 0b10 != 0
Graphics.update
sleep(0.5 / Graphics.frame_rate)
end
Graphics.frame_count = frame_count
Graphics.frame_rate = frame_rate
sprite.bitmap.dispose
sprite.dispose
end
# メイン処理
## 識別子
refresh_progress.call("signature")
signature = "\211PNG\r\n\C-z\n"
## IHDR チャンク
refresh_progress.call("image header")
packed_IHDR = [
self.width, # 幅
self.height, # 高さ
8, # ビット深度 [ピクセル/サンプル]
6, # カラータイプ
0, # 圧縮方式
0, # フィルター方式
0 # インターレース方式
]
data_IHDR = packed_IHDR.pack("NNccccc")
chunk_IHDR = String.new
chunk_IHDR << [data_IHDR.length].pack("N")
chunk_IHDR << "IHDR"
chunk_IHDR << data_IHDR
chunk_IHDR << [crc("IHDR" + data_IHDR)].pack("N")
## pHYs チャンク
refresh_progress.call("physical pixel dimensions")
resolution =
[self.width * 10, Integer(self.height * 5.0 * Math.sqrt(2.0))].max
packed_pHYs = [
resolution, # 横解像度 [ピクセル/単位]
resolution, # 縦解像度
1 # 単位 (0:不明 1:メートル)
]
data_pHYs = packed_pHYs.pack("NNC")
chunk_pHYs = String.new
chunk_pHYs << [data_pHYs.length].pack("N")
chunk_pHYs << "pHYs"
chunk_pHYs << data_pHYs
chunk_pHYs << [crc("pHYs" + data_pHYs)].pack("N")
## IDAT チャンク
refresh_progress.call("image data")
packed_IDAT = [
0x78, # 圧縮方式とフラグ
(04 << 5), # フラグ
String.new, # 圧縮されたデータ
0 # ADLER32 チェックサム
]
# イメージサンプル
image_sample = String.new
self.height.times do |y|
refresh_progress.call(sprintf("image data (creating sample:%d%%)",
y * 100 / self.height))
# フィルター形式
image_sample << 0
self.width.times do |x|
# ピクセルの色
pixel = self.get_pixel(x, y)
image_sample << pixel.red.to_i
image_sample << pixel.green.to_i
image_sample << pixel.blue.to_i
image_sample << pixel.alpha.to_i
end
end
# zlib デフレート・インフレート圧縮
refresh_progress.call("image data (compressing)")
packed_IDAT[1] |= 31 - (packed_IDAT[0]*256 + packed_IDAT[1]) % 31
deflate_blocks = Array.new
nth = 0
while nth < image_sample.length
len = [image_sample.length - nth, 0xFFFF].min
deflate_blocks << [len, ~len, image_sample[nth, len]].pack("vva*")
nth += 0xFFFF
end
for block in deflate_blocks
header = 0
header |= 1 << 2 if block.equal?(deflate_blocks[-1])
packed_IDAT[2] << (header << 5)
packed_IDAT[2] << block
end
refresh_progress.call("image data (check sum)")
s1 = 1
s2 = 0
image_sample.each_byte {|ch| s2 += s1 += ch }
s1 %= 65521
s2 %= 65521
packed_IDAT[3] = s2*65536 + s1
refresh_progress.call("image data")
data_IDAT = packed_IDAT.pack("CCa*N") # 圧縮データ
chunk_IDAT = String.new
chunk_IDAT << [data_IDAT.length].pack("N")
chunk_IDAT << "IDAT"
chunk_IDAT << data_IDAT
chunk_IDAT << [crc("IDAT" + data_IDAT)].pack("N")
## IEND チャンク
refresh_progress.call("image trailer")
chunk_IEND = String.new
chunk_IEND << [0].pack("N")
chunk_IEND << "IEND"
chunk_IEND << [crc("IEND")].pack("N")
## 書く
refresh_progress.call("")
out = file.instance_of?(String) ? File.open(file, "wb") : file
out.print(
signature + chunk_IHDR + chunk_pHYs + chunk_IDAT + chunk_IEND)
out.close if file.instance_of?(String)
processing = false
thread_which_update_frame.run.join
rescue SystemCallError
processing = false
$@ = caller
thread_which_update_frame.run.join
raise
end
end
end
戻る