Ich hatte dazu mal ein Script geschrieben:
Code:
class PngWriter
PNGException = Class.new(RuntimeError)
# Color types
INDEXED = 3
RGB = 2
RGBA = 6
# Filter methods
NONE = 0
attr_accessor :header, :bitmap
attr_reader :chunks, :mode, :alpha_color, :palette, :palette_offset, :filter_method, :bit_depth
# @param [Bitmap] pixeldata
def initialize bitmap
@bitmap = bitmap
@header = Header.new self
@chunks = []
@mode = RGBA
@bit_depth = 8
@palette = {}
@palette_offset = 0
@palette_size = 0
@filter_method = NONE # currently not implemented
end
# @param [INDEXED, RGB, RGBA, NONE] mode color type constant
def set_mode mode
@mode = mode
end
# set PngWriter to rgba mode. In this mode PngWriter stores each pixel as 32 bit bytestring
def use_rgba
set_mode RGBA
end
# set PngWriter to rgb mode. In this mode PngWriter stores each pixel as 24 bit bytestring
# if you choose rgb mode, all transparency information will be lost
def use_rgb
set_mode RGB
end
# set PngWriter to indexing mode. In this mode PngWriter stores each used color
# into a 24 bit table. The pixel values in the images are stored as <=8 bit table index.
def use_indexing
set_mode INDEXED
end
# writes the image to the given file
# @param [String] filename path to the file
def write_to_file filename
File.open(filename, "wb") {|io| write io }
end
# writes the image to the given buffer
# @param [String, IO] io Object which responds to #<<(String)
def write io
write_header io
@header.write io
if @mode == INDEXED
compute_palette
Palette.new(self).write io
Transparency.new(self).write io if @palette_offset >= 0
end
Image.new(self).write io
@chunks.each {|chunk| chunk.write io}
Eof.new(self).write io
io
end
private
def write_header buffer
buffer << [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A].pack("C8")
end
def compute_palette
@palette.clear
index = 0
alpha_index = 1
(0...bitmap.height).each do |y|
(0...bitmap.width).each do |x|
color = bitmap.get_pixel(x,y)
if !@palette.has_key?(color) then
if color.alpha == 255 then
@palette[bitmap.get_pixel(x,y)] = (index+=1)
else
@palette[bitmap.get_pixel(x,y)] = (alpha_index-=1)
end
end
end
end
@palette_offset = -alpha_index
max = (1<<@bit_depth)
raise PNGException.new("Indexed palette has more than #{max} entries") if @palette.size > max
end
class Chunk
def data
""
end
def initialize png
@png = png
end
def write buffer
chunk_data = data
fields = chunk_name << chunk_data
crc = [Zlib.crc32(fields)].pack("N")
buffer << [chunk_data.length].pack("N") << fields << crc
end
end
class Header < Chunk
def chunk_name
"IHDR"
end
def data
bitmap = @png.bitmap
color_type = @png.mode
bit_depth = @png.bit_depth
interlace_method = 0
[bitmap.width, bitmap.height, bit_depth, color_type, 0, 0, interlace_method].pack("N2C5")
end
end
class Palette < Chunk
def chunk_name
"PLTE"
end
def data
palette = @png.palette
offset = @png.palette_offset
buffer = "\00" * (palette.size * 3)
palette.each do |key, value|
i = (value+offset)*3
buffer[i] = Integer(key.red).chr
buffer[i+1] = Integer(key.green).chr
buffer[i+2] = Integer(key.blue).chr
end
buffer
end
end
class Transparency < Chunk
def chunk_name
"tRNS"
end
def data
buffer = "\00" * (@png.palette_offset+1)
@png.palette.each do |color, value|
buffer[-value] = Integer(color.alpha).chr if value <= 0
end
buffer
end
end
class Image < Chunk
def chunk_name
"IDAT"
end
def data
compress(@png.mode == INDEXED ? scanlines_indexed : scanlines_rgba)
end
def scanlines_indexed
bitmap = @png.bitmap
palette = @png.palette
offset = @png.palette_offset
buffer = "\00" * ((bitmap.width+1) * bitmap.height)
filter = NONE
i = -1
(0...bitmap.height).each do |y|
buffer[i+=1] = filter
(0...bitmap.width).each do |x|
buffer[i+=1] = palette[bitmap.get_pixel(x, y)] + offset
end
end
buffer
end
def scanlines_rgba
bitmap = @png.bitmap
alpha = @png.mode == RGBA
buffer = "\00" * (bitmap.width * bitmap.height * (alpha ? 4 : 3) + bitmap.height)
filter = @png.filter_method
i = -1
(0...bitmap.height).each do |y|
buffer[i+=1] = filter
(0...bitmap.width).each do |x|
color = bitmap.get_pixel(x, y)
buffer[i+=1] = Integer(color.red).chr
buffer[i+=1] = Integer(color.green).chr
buffer[i+=1] = Integer(color.blue).chr
buffer[i+=1] = Integer(color.alpha).chr if alpha
end
end
buffer
end
def compress buffer
Zlib::Deflate.deflate(buffer)
end
end
class Eof < Chunk
def chunk_name
"IEND"
end
end
module Filters
end
end
class Color
# returns a hash value for color objects
# @return [Fixnum]
def hash
(Integer(red) << 22) ^ (Integer(green) << 14) ^ (Integer(blue) << 6) ^ Integer(alpha)
end
end
Einfach in Scripteditor kopieren. Verwendung funktioniert folgendermaßen:
Code:
# laden eines beliebigen Bildes
bitmap = RPG::Cache.picture("mein_picture")
png = PngWriter.new(bitmap)
png.write_to_file("mein_bild.png")
Du kannst Grafiken in drei verschiedenen Modi abspeichern: RGB, RGBA und INDEXED. RGB braucht 24 bit pro Pixel, hat dafür aber keine Transparenz. RGBA braucht 32 bit pro Pixel. INDEXED braucht nur 8 bit pro Pixel, dafür sind aber nur 256 Farben pro Bild erlaubt. Außerdem ist INDEXED etwas langsamer beim Speichern der Datei als die anderen beiden Verfahren. Die Grafiken in der RTP sind üblicherweise im INDEXED Modus gespeichert. Der Speicherplatzunterschied zwischen RGB, RGBA und INDEXED ist allerdings nicht soo extrem hoch (da die Bilddaten hinterher eh komprimiert werden), von daher ist es egal was du verwendest. Generell gilt: Alle drei Verfahren sind recht langsam (brauchen ~200 ms für mittel große Bilder). Daher nicht jeden Frame aufrufen ^^°
Die Modi setzt du folgenderweise:
Code:
png = PngWriter.new(bitmap)
# je nachdem welchen Modus du willst
png.use_rgba # STANDARD
png.use_rgb
png.use_indexing