PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Sprite to File



Doktor von Stein
27.10.2011, 08:03
Wie kann man einen Sprite, der im Spiel gezeichnet wurde, in ein neues Bild im Spieleordner "konvertieren"? Meine Kentnisse mit dem File-Objekt reichen dazu nicht aus.

-KD-
27.10.2011, 16:31
Ich hatte dazu mal ein Script geschrieben:

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:

# 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:

png = PngWriter.new(bitmap)
# je nachdem welchen Modus du willst
png.use_rgba # STANDARD
png.use_rgb
png.use_indexing

Doktor von Stein
27.10.2011, 18:34
Danke.