Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Apng D:
ich wollte passend zum "Bitmap to PNG" Skript auch ein "Bitmaps to APNG"-Skript schreiben.
Hat natürlich nicht geklappt :D
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
#============================================================================ # ** Bitmap -> .PNG #---------------------------------------------------------------------------- # www.66rpg.com # V 1.1 # 16/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Saves a Bitmap as a .png file. # # Bitmap#make_png([path]) # Saves the Bitmap to path. Eventually given directories will be created # if needed. #============================================================================ class Bitmap def make_png(path = 'like') Graphics.long_code do File.makedirs(File.dirname(path)) data = Zlib::Png_File.make_png(self) if File.extname(path) != '.png' path += '.png' end File.open(path, 'wb') do |file| file.write(data) end end end end module Graphics def self.long_code(change_frames = true, &block) sleeptime = 2.5 t = Thread.new do if change_frames loop do sleep(sleeptime) Graphics.update end else loop do sleep(sleeptime) Graphics.update Graphics.frame_count -= 1 end end end result = yield t.exit t = nil return result end end module Zlib class Png_File < GzipWriter def Png_File.make_png(bitmap) self.open('temp.gz') do |gz| gz.make_png(bitmap) end data = nil GzipReader.open('temp.gz') do |gz| data = gz.read end File.delete('temp.gz') return data end def make_png(bitmap) list = [ Png_File.png_header, Png_File.png_ihdr(bitmap), Png_File.png_idat(bitmap), Png_File.png_iend ] list.each do |i| write(i) end end # Constants PNG_HEADER_ARRAY = [137, 80, 78, 71, 13, 10, 26, 10] PNG_HEADER_STRING = PNG_HEADER_ARRAY.pack('C*') IHDR_SIZE = [13].pack('N') IHDR_SIGN = 'IHDR' IHDR_DEPTH = [8].pack('C') IHDR_COLOR = [6].pack('C') IHDR_COMP = [0].pack('C') IHDR_FILTER = [0].pack('C') IHDR_INTERL = [0].pack('C') IHDR_WIDTHHEIGHT = Proc.new {|i| [i].pack('N')} IHDR_CRC = Proc.new {|i| [Zlib.crc32(i)].pack('N')} IDAT_HEADER = "\x49\x44\x41\x54" IDAT_COMPDATA = Proc.new {|i| Zlib::Deflate.deflate(i, 8)} IDAT_CRC = IHDR_CRC IDAT_SIZE = Proc.new {|i| [i.length].pack('N')} DATA_MAX_ARRAY = 2**28 - 5 IEND_SIZE = [0].pack('N') IEND_SIGN = 'IEND' IEND_CRC = IDAT_CRC # Methods def self.png_header return PNG_HEADER_STRING.dup end def self.png_ihdr(bitmap) width = IHDR_WIDTHHEIGHT.call(bitmap.width) height = IHDR_WIDTHHEIGHT.call(bitmap.height) string = IHDR_SIGN + width + height + IHDR_DEPTH + IHDR_COLOR + IHDR_COMP + IHDR_FILTER + IHDR_INTERL crc = IHDR_CRC.call(string) return IHDR_SIZE + string + crc end def self.png_idat(bitmap) cdat = IDAT_COMPDATA.call(self.png_make_bitmap_data(bitmap)) return IDAT_SIZE.call(cdat) + IDAT_HEADER + cdat + IDAT_CRC.call(IDAT_HEADER + cdat) end def self.png_make_bitmap_data(bitmap) gz = Zlib::GzipWriter.open('hoge.gz') w, h = bitmap.width, bitmap.height data = [] 0.upto(h) do |y| data.push(0) 0.upto(w) do |x| c = bitmap.get_pixel(x, y) data.push(c.red) data.push(c.green) data.push(c.blue) data.push(c.alpha) if data.size > DATA_MAX_ARRAY gz.write(data.pack('C*')) data.clear end end end gz.write(data.pack('C*')) data.clear gz.close GC.start data = nil Zlib::GzipReader.open('hoge.gz') do |gz| data = gz.read end File.delete('hoge.gz') return data end def self.png_iend return IEND_SIZE + IEND_SIGN + IEND_CRC.call(IEND_SIGN) end end end class File def File.makedirs(*dirs) verbose = if dirs[-1].is_a? String then false else dirs.pop end mode = 0755 for dir in dirs parent = dirname(dir) next if parent == dir or directory? dir makedirs parent unless directory? parent $stderr.print "mkdir ", dir, "\n" if verbose if basename(dir) != "" begin Dir.mkdir dir, mode rescue SystemCallError raise unless directory? dir end end end end end |
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
#============================================================================ # ** Bitmaps -> .APNG #---------------------------------------------------------------------------- # Neo-Bahamut # V 1.0 # 16/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Requires Bitmap -> .PNG! #---------------------------------------------------------------------------- # Bitmap.to_apng(array_of_bitmaps, options[, path]) # array_of_bitmaps is an Array with the Bitmaps which will be converted into # an APNG. # options is a Hash defining the single frame options (see below). # path is optional and defines the path to the saved file. # # Options: # 'plays' => Times the animation is played. 0 for infinitif. # 'frames' => Another Hash having the frame IDs as keys and # even more hashs as values :) (see below again) (optional) # # Options -> Frames: # 'time' => Time the frame is shown. (in seconds) # 'x' / 'y' => x and y offset # 'dispose' => Type of dispose which is used before rendering the next frame. # 0: Nothing is disposed. # 1: Cleared to full transparency. # 2: Changed to the prevoius content. # 'blend' => Type of blending which is used when rendering the frame. # 0: Overwrites everything. # 1: Blends over the old frame. #============================================================================ class Bitmap def Bitmap.to_apng(list, options, path = 'alike') Graphics.long_code do File.makedirs(File.dirname(path)) data = Zlib::APng_File.make_apng(list, options) if File.extname(path) != '.png' path += '.png' end File.open(path, 'wb') do |file| file.write(data) end end end end module Zlib class APng_File < GzipWriter def APng_File.make_apng(bitmaps, options) self.open('temp.gz') do |gz| gz.make_apng(bitmaps, options) end data = nil GzipReader.open('temp.gz') do |gz| data = gz.read end File.delete('temp.gz') return data end def make_apng(bitmaps, options) list = [ Png_File.png_header, Png_File.png_ihdr(bitmaps[0]), APng_File.apng_actl(bitmaps, options), APng_File.apng_fctl(bitmaps[0], options['frames'], 0), Png_File.png_idat(bitmaps[0]) ] bitmaps.each_index do |n| b, o = bitmaps[n], options[n] list.push(APng_File.apng_fctl(bitmaps[n], options['frames'], n)) list.push(APng_File.apng_fdat(bitmaps[n], n)) end list.push(Png_File.png_iend) list.each do |i| write(i) end end # Methods def self.apng_actl(bitmaps, options) [bitmaps.size, options['plays']].pack('II') end def self.apng_fctl(bitmap, option, n) if option.nil? option = EMPTY_HASH else option = option[n] end x, y, time, dis, ble = option['x'], option['y'], option['time'], option['dispose'], option['blend'] x ||= 0 y ||= 0 time ||= 1 dis ||= 0 ble ||= 0 den = 10 ** time.dec_place [n, bitmap.width, bitmap.height, x, y, time*den, den, dis, ble].pack('IIIIISSCC') end def self.apng_fdat(bitmap, n) data = Png_File.png_idat(bitmap) return [n].pack('I') + data end # Constants EMPTY_HASH = Hash.new end end class Numeric def dec_place if self.is_a?(Integer) return 0 else return self.to_s.split('.')[1].length end end end |
Testcode:
|
|
Ruby Quellcode |
1 2 |
Bitmap.to_apng([Bitmap.new('test/1')], {'plays' => 0}) exit |
Die beiden Skript nochmal in Pastebin:
http://wurstinator.pastebin.com/YFBKZnrs
http://wurstinator.pastebin.com/q4RuYQgv
Quellen, aus denen ich meine Informationen bezogen habe:
http://en.wikipedia.org/wiki/APNG
https://wiki.mozilla.org/APNG_Specificat…equence_Numbers
Das Problem ist, das scheinbar irgendein paar Bytes falsch sind, da das Bild von keinem Programm angezeigt werden kann.

ANIM1.PNG besteht aus FRAME1.PNG und FRAME2.PNG, welche du hier finden kannst. Kleine Testdaten zu haben erleichtert die Sache und ein kleiner Hex Editor ist natürlich auch fein wie du siehst
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Ich hab den Code jetzt etwas verändert, aber immer noch bringen die Chunks Fehler.
Der Aufbau von acTL müsste doch so sein:
Bytes 0-3: 'acTL'
Bytes 4-7: Ein Integer, der die Frameanzahl zeigt
Bytes 8-11: Ein Integer, der die Wiedergabezahl zeigt
|
|
Ruby Quellcode |
1 |
[97, 99, 84, 76, bitmaps.size, options['plays']].pack('CCCCII') |
bitmaps.size ist 1
options['plays'] ist 0
Aber es klappt nicht :(
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Also, bei der Windows Bild- und Faxanzeige wird jetzt der erste Frame angezigt, wie es sein sollte ^^
Aber Firefox sagt immer, dass die Datei Fehler enthält.
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#============================================================================ # ** Bitmaps -> .APNG #---------------------------------------------------------------------------- # Neo-Bahamut # V 1.0 # 16/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Requires Bitmap -> .PNG! #---------------------------------------------------------------------------- # Bitmap.to_apng(array_of_bitmaps, options[, path]) # array_of_bitmaps is an Array with the Bitmaps which will be converted into # an APNG. # options is a Hash defining the single frame options (see below). # path is optional and defines the path to the saved file. # # Options: # 'plays' => Times the animation is played. 0 for infinitif. # 'frames' => Another Hash having the frame IDs as keys and # even more hashs as values :) (see below again) (optional) # # Options -> Frames: # 'time' => Time the frame is shown. (in seconds) # 'x' / 'y' => x and y offset # 'dispose' => Type of dispose which is used before rendering the next frame. # 0: Nothing is disposed. # 1: Cleared to full transparency. # 2: Changed to the prevoius content. # 'blend' => Type of blending which is used when rendering the frame. # 0: Overwrites everything. # 1: Blends over the old frame. #============================================================================ class Bitmap def Bitmap.to_apng(list, options, path = 'alike') Graphics.long_code do File.makedirs(File.dirname(path)) data = Zlib::APng_File.make_apng(list, options) if File.extname(path) != '.png' path += '.png' end File.open(path, 'wb') do |file| file.write(data) end end end end module Zlib class APng_File < GzipWriter def APng_File.make_apng(bitmaps, options) self.open('temp.gz') do |gz| gz.make_apng(bitmaps, options) end data = nil GzipReader.open('temp.gz') do |gz| data = gz.read end File.delete('temp.gz') return data end def make_apng(bitmaps, options) if bitmaps.size == 1 self.write(Png_File.png_header) self.write(Png_File.png_ihdr(bitmaps[0])) self.write(Png_File.png_idat(bitmaps[0])) self.write(Png_File.png_iend) return end list = [ Png_File.png_header, Png_File.png_ihdr(bitmaps[0]), APng_File.apng_actl(bitmaps, options), APng_File.apng_fctl(bitmaps[0], options['frames'], 0), Png_File.png_idat(bitmaps[0]) ] rest = bitmaps[1, bitmaps.size-1] rest.each_index do |n| b, o = bitmaps[n], options[n] list.push(APng_File.apng_fctl(bitmaps[n], options['frames'], n)) list.push(APng_File.apng_fdat(bitmaps[n], n)) end list.push(Png_File.png_iend) list.each do |i| write(i) end end # Methods def self.make_chunk(type, data) size = [data.byte_size].pack('N') crc = [Zlib.crc32(type + data)].pack('N') return size + type + data + crc end def self.apng_actl(bitmaps, options) data = [bitmaps.size, options['plays']].pack('NN') self.make_chunk('acTL', data) end EMPTY_HASH = Hash.new def self.apng_fctl(bitmap, options, n) option = options.nil? ? EMPTY_HASH : options[n] x = option['x']; x ||= 0 y = option['y']; y ||= 0 time = option['time']; time ||= 1 dis = option['dis']; dis ||= 0 ble = option['ble']; ble ||= 0 den = 10 ** time.dec_place data = [n, bitmap.width, bitmap.height, x, y, time*den, den, dis, ble].pack('NNNNNSSCC') self.make_chunk('fcTL', data) end def self.apng_fdat(bitmap, n) mdat = Png_File.png_make_bitmap_data(bitmap) data = Zlib::Deflate.deflate(mdat, 8) self.make_chunk('fdAT', [n].pack('N') + data) end end end class Numeric def dec_place if self.is_a?(Integer) return 0 else return self.to_s.split('.')[1].length end end end |
Ich glaub, dass hat was mit fdAT zu tun, weil ich nicht so wirklich weiß, was ich da alles komprimieren soll...
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
#============================================================================ # ** Bitmap -> .PNG #---------------------------------------------------------------------------- # www.66rpg.com & Neo-Bahamut # V 1.1.2 # 18/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Saves a Bitmap as a .png file. # # Bitmap#make_png([path]) # Saves the Bitmap to path. Eventually given directories will be created # if needed. #============================================================================ class Bitmap def make_png(path = 'like') Graphics.long_code do File.makedirs(File.dirname(path)) data = Zlib::Png_File.make_png(self) if File.extname(path) != '.png' path += '.png' end File.open(path, 'wb') do |file| file.write(data) end end end end module Graphics def self.long_code(change_frames = true, &block) sleeptime = 2.5 t = Thread.new do if change_frames loop do sleep(sleeptime) Graphics.update end else loop do sleep(sleeptime) Graphics.update Graphics.frame_count -= 1 end end end result = yield t.exit t = nil return result end end class String def byte_size unpack('C*').size end alias_method :byte_length, :byte_size end module Zlib class Png_File < GzipWriter def Png_File.make_png(bitmap) self.open('temp.gz') do |gz| gz.make_png(bitmap) end data = nil GzipReader.open('temp.gz') do |gz| data = gz.read end File.delete('temp.gz') return data end def make_png(bitmap) list = [ Png_File.png_header, Png_File.png_ihdr(bitmap), Png_File.png_idat(bitmap), Png_File.png_iend ] list.each do |i| write(i) end end # Methods def self.make_chunk(type, data) size = [data.byte_size].pack('N') crc = [Zlib.crc32(type + data)].pack('N') return size + type + data + crc end PNG_HEADER = [137, 80, 78, 71, 13, 10, 26, 10].pack('C*') def self.png_header PNG_HEADER end def self.png_ihdr(bitmap) data = [bitmap.width, bitmap.height, 8, 6, 0, 0, 0].pack('NNCCCCC') return self.make_chunk('IHDR', data) end def self.png_idat(bitmap) data = self.png_make_bitmap_data(bitmap) return self.make_chunk('IDAT', Zlib::Deflate.deflate(data, 8)) end DATA_MAX_ARRAY = 2**28 - 5 def self.png_make_bitmap_data(bitmap) gz = Zlib::GzipWriter.open('hoge.gz') w, h = bitmap.width-1, bitmap.height-1 data = [] 0.upto(h) do |y| data.push(0) 0.upto(w) do |x| c = bitmap.get_pixel(x, y) data.push(c.red) data.push(c.green) data.push(c.blue) data.push(c.alpha) if data.size > DATA_MAX_ARRAY gz.write(data.pack('C*')) data.clear end end end gz.write(data.pack('C*')) data.clear gz.close GC.start data = nil Zlib::GzipReader.open('hoge.gz') do |gz| data = gz.read end File.delete('hoge.gz') return data end def self.png_iend return self.make_chunk('IEND', '') end end end class File def File.makedirs(*dirs) verbose = if dirs[-1].is_a? String then false else dirs.pop end mode = 0755 for dir in dirs parent = dirname(dir) next if parent == dir or directory? dir makedirs parent unless directory? parent $stderr.print "mkdir ", dir, "\n" if verbose if basename(dir) != "" begin Dir.mkdir dir, mode rescue SystemCallError raise unless directory? dir end end end end end |
Ein mit diesem Skript erstellte APNG mit 3 Frames sollte so aussehen:
PNG-Header -> acTL -> fcTL (Nr. 0) -> IDAT -> fcTL (Nr. 1) -> fdAT (Nr. 2) -> fcTL (Nr. 3) -> fdAT (Nr. 4) -> IEND
So ist es derzeit:
PNG-Header -> acTL -> fcTL (Nr. 0) -> IDAT -> fcTL (Nr. 1) -> fdAT (Nr. 2) -> fcTL (Nr. 2) -> fdAT (Nr. 3) -> IEND
2. Problem: make_apng
So wie ich das sehe wird das falsche Bitmap (der 1. Frame, der bereits geschrieben wurde (IDAT)) wird nochmals als 2. Frame geschrieben, etc.) genommen:
|
|
Ruby Quellcode |
1 2 3 |
rest = bitmaps[1, bitmaps.size-1] rest.each_index do |n| b, o = bitmaps[n], options[n] # es wird bitmap[0] genommen und nicht rest[0], bitmap[1] / each_with_index verwenden? |
3. Problem: fcTL chunk
Ich müsste die Doku mir noch mal ansehen, aber das dürfte eigentlich so passen bis auf eine Kleinigkeit: im String SS durch nn oder n2 ersetzen.
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Vielen Dank, Cremno, wer weiß, ob ich es ohne dich geschafft hätte? :D
Und hier das Ergebnis, für das sich die ganze Mühe ja wohl gelohnt hat xD"
Zitat von »Neo-Bahamut«
3 war richtig (die beiden Zahlen wurden ja als unsigned Shots beschrieben)
S ist aber unter Windows Little Endian und nicht Network-/Big Endian, n jedoch:
Zitat von »https://wiki.mozilla.org/APNG_Specification#Terminology«
an "unsigned short" shall be a 16-bit unsigned integer in network byte order [...]
Immer schön an die Spezifikation halten
Zitat von »Ruby Doku«
n | Short, network (big-endian) byte-order

Ansonsten Glückwunsch zum funktionieren Skript!
wenn ein objekt nicht gemashalt werden kann, geht mir das gewaltig auf den Keks ...
(weil procs nicht save bar sind, musste ich einige meiner engines umschreiben)
Alles ist wahr, wenn man für wahr einen bestimmten Wert annimmt.
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Weil mit S klappts ja auch^^"
@ Hanmac: Das ist ein anderes Thema, aber Proc zu "marshalen" geht nicht :p
@mashal: genau das war ja das problem
auserdem das ich probleme hatte wenn ich meine game items und rpg items gespeichert hatte, waren die rpg items die sich in den game items nich befanden nicht mehr gleich mit den rpg items die ich extra gespeichert hatte (aber das hab ich gefixt)
Alles ist wahr, wenn man für wahr einen bestimmten Wert annimmt.
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
#============================================================================ # ** Bitmaps -> .APNG #---------------------------------------------------------------------------- # Neo-Bahamut # thanks to Cremno for debugging # V 1.0 # 19/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Requires Bitmap -> .PNG! #---------------------------------------------------------------------------- # Bitmap.to_apng(array_of_bitmaps, options[, path]) # array_of_bitmaps is an Array with the Bitmaps which will be converted into # an APNG. # options is a Hash defining the single frame options (see below). # path is optional and defines the path to the saved file. # # Options: # 'plays' => Times the animation is played. 0 for infinitif. # 'frames' => Another Hash having the frame IDs as keys and # even more hashs as values :) (see below again) (optional) # # Options -> Frames: # 'time' => Time the frame is shown. (in seconds) # 'x' / 'y' => x and y offset # 'dispose' => Type of dispose which is used before rendering the next frame. # 0: Nothing is disposed. # 1: Cleared to full transparency. # 2: Changed to the prevoius content. # 'blend' => Type of blending which is used when rendering the frame. # 0: Overwrites everything. # 1: Blends over the old frame. #============================================================================ class Bitmap def Bitmap.to_apng(list, options, path = 'alike') Graphics.long_code do File.makedirs(File.dirname(path)) data = Zlib::APng_File.make_apng(list, options) if File.extname(path) != '.png' path += '.png' end File.open(path, 'wb') do |file| file.write(data) end end end end module Zlib class APng_File < GzipWriter def APng_File.make_apng(bitmaps, options) self.open('temp.gz') do |gz| gz.make_apng(bitmaps, options) end data = nil GzipReader.open('temp.gz') do |gz| data = gz.read end File.delete('temp.gz') return data end def make_apng(bitmaps, options) if bitmaps.size == 1 self.write(Png_File.png_header) self.write(Png_File.png_ihdr(bitmaps[0])) self.write(Png_File.png_idat(bitmaps[0])) self.write(Png_File.png_iend) return end list = [ Png_File.png_header, Png_File.png_ihdr(bitmaps[0]), APng_File.apng_actl(bitmaps, options), APng_File.apng_fctl(bitmaps[0], options['frames'], 0, 0), Png_File.png_idat(bitmaps[0]) ] 1.upto(bitmaps.size-1) do |n| b, o = bitmaps[n], options[n] list.push(APng_File.apng_fctl(bitmaps[n], options['frames'], n, n*2-1)) list.push(APng_File.apng_fdat(bitmaps[n], n*2)) end list.push(Png_File.png_iend) list.each do |i| write(i) end end # Methods def self.make_chunk(type, data) size = [data.byte_size].pack('N') crc = [Zlib.crc32(type + data)].pack('N') return size + type + data + crc end def self.apng_actl(bitmaps, options) data = [bitmaps.size, options['plays']].pack('NN') self.make_chunk('acTL', data) end EMPTY_HASH = Hash.new def self.apng_fctl(bitmap, options, n, seqnum) option = (options.nil? || options[n].nil?) ? EMPTY_HASH : options[n] x = option['x']; x ||= 0 y = option['y']; y ||= 0 time = option['time']; time ||= 1 dis = option['dis']; dis ||= 0 ble = option['ble']; ble ||= 0 den = 10 ** time.dec_place data = [seqnum, bitmap.width, bitmap.height, x, y, time*den, den, dis, ble].pack('NNNNNSSCC') self.make_chunk('fcTL', data) end def self.apng_fdat(bitmap, seqnum) mdat = Png_File.png_make_bitmap_data(bitmap) data = Zlib::Deflate.deflate(mdat, 8) self.make_chunk('fdAT', [seqnum].pack('N') + data) end end end class Numeric def dec_place if self.is_a?(Integer) return 0 else return self.to_s.split('.')[1].length end end end |
sag nur wenn du fragen hast
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
#============================================================================ # ** Bitmap -> .PNG #---------------------------------------------------------------------------- # www.66rpg.com & Neo-Bahamut # V 1.1.2 # 18/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Saves a Bitmap as a .png file. # # Bitmap#make_png([path]) # Saves the Bitmap to path. Eventually given directories will be created # if needed. #============================================================================ class Bitmap def make_png(path) Graphics.long_code do File.makedirs(File.dirname(path)) path.concat('.png') if File.extname(path) != '.png' File.open(path, 'wb') { |file| file.write(Zlib::Png_File.make_png(self)) } end end end module Graphics def self.long_code(change_frames = true, &block) sleeptime = 2.5 t = Thread.new do if change_frames loop do sleep(sleeptime) Graphics.update end else loop do sleep(sleeptime) Graphics.update Graphics.frame_count -= 1 end end end result = yield t.exit t = nil return result end end module Zlib class Png_File < GzipWriter def self.make_png(bitmap) self.open('temp.gz') { |gz| gz.make_png(bitmap) } data = GzipReader.open('temp.gz') { |gz| gz.read } File.delete('temp.gz') return data end def make_png(bitmap) list = [ png_header, png_ihdr(bitmap), png_idat(bitmap), png_iend ] list.each { |i| write(i) } end # Methods def make_chunk(type, data) return [data.unpack('C*').size].pack('N') + type + data + [Zlib.crc32(type + data)].pack('N') end def png_header return "\211PNG\r\n\032\n" #png_header end def png_ihdr(bitmap) data = [bitmap.width, bitmap.height, 8, 6, 0, 0, 0].pack('NNCCCCC') return make_chunk('IHDR', data) end def png_idat(bitmap) data = png_make_bitmap_data(bitmap) return make_chunk('IDAT', Zlib::Deflate.deflate(data, 8)) end def png_make_bitmap_data(bitmap) Zlib::GzipWriter.open('hoge.gz') do |gz| data = [] bitmap.height.times do |y| data.push(0) bitmap.width.times do |x| c = bitmap.get_pixel(x, y) data.push(c.red,c.green,c.blue,c.alpha) if data.size > 268435451 #DATA_MAX_ARRAY gz.write(data.pack('C*')) data.clear end end end gz.write(data.pack('C*')) end GC.start data = Zlib::GzipReader.open('hoge.gz') { |gz| gz.read } File.delete('hoge.gz') return data end def png_iend return make_chunk('IEND', '') end end end class File def self.makedirs(*dirs) verbose = if dirs[-1].is_a? String then false else dirs.pop end mode = 0755 for dir in dirs parent = dirname(dir) next if parent == dir or directory? dir makedirs parent unless directory? parent $stderr.print "mkdir ", dir, "\n" if verbose if basename(dir) != "" begin Dir.mkdir dir, mode rescue SystemCallError raise unless directory? dir end end end end end |
|
|
Ruby Quellcode |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
#============================================================================ # ** Bitmaps -> .APNG #---------------------------------------------------------------------------- # Neo-Bahamut # thanks to Cremno for debugging # V 1.0 # 19/04/2010 (DD/MM/YYYY) #---------------------------------------------------------------------------- # Requires Bitmap -> .PNG! #---------------------------------------------------------------------------- # Bitmap.to_apng(array_of_bitmaps, options[, path]) # array_of_bitmaps is an Array with the Bitmaps which will be converted into # an APNG. # options is a Hash defining the single frame options (see below). # path is optional and defines the path to the saved file. # # Options: # 'plays' => Times the animation is played. 0 for infinitif. # 'frames' => Another Hash having the frame IDs as keys and # even more hashs as values :) (see below again) (optional) # # Options -> Frames: # 'time' => Time the frame is shown. (in seconds) # 'x' / 'y' => x and y offset # 'dispose' => Type of dispose which is used before rendering the next frame. # 0: Nothing is disposed. # 1: Cleared to full transparency. # 2: Changed to the prevoius content. # 'blend' => Type of blending which is used when rendering the frame. # 0: Overwrites everything. # 1: Blends over the old frame. #============================================================================ class Bitmap def self.to_apng(list, path, options = {} ) Graphics.long_code do File.makedirs(File.dirname(path)) path.concat('.png') if File.extname(path) != '.png' File.open(path, 'wb') { |file| file.write(Zlib::APng_File.make_apng(list, options)) } end end def to_apng(path,options = {} ) Bitmap.to_apng([self], path,options) end end module Zlib class APng_File < Png_File def self.make_apng(bitmaps, options) self.open('temp.gz') { |gz| gz.make_apng(bitmaps, options) } data = GzipReader.open('temp.gz') { |gz| gz.read } File.delete('temp.gz') return data end def make_apng(bitmaps, options) return make_png(bitmaps[0])) if bitmaps.size == 1 bitmap = bitmaps.shift options['frames'] ||={} list = [ png_header, png_ihdr(bitmap), apng_actl(bitmap, options['plays'] || 0), apng_fctl(bitmap, options['frames'][0], 0), png_idat(bitmap) ] bitmaps.each_with_index { |b,n| list.push(apng_fctl(b, options['frames'][n], n*2-1),apng_fdat(b, n*2))} list.push(png_iend) list.each { |i| write(i) } end end # Methods def apng_actl(bitmaps, plays) return make_chunk('acTL', [bitmaps.size, plays].pack('NN')) end def apng_fctl(bitmap, option, seqnum) option ||= {} x = option['x'] || 0 y = option['y'] || 0 time = option['time'] || 1 dis = option['dis'] || 0 ble = option['ble'] || 0 den = 10 ** [(time % 1).to_s.size - 2,0].max data = [seqnum, bitmap.width, bitmap.height, x, y, time*den, den, dis, ble].pack('NNNNNSSCC') return make_chunk('fcTL', data) end def apng_fdat(bitmap, seqnum) mdat = png_make_bitmap_data(bitmap) data = Zlib::Deflate.deflate(mdat, 8) return make_chunk('fdAT', [seqnum].pack('N') + data) end end |
Alles ist wahr, wenn man für wahr einen bestimmten Wert annimmt.
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Was hast du denn geändert? ^^
Zitat
sag nur wenn du fragen hast
und die methoden sind alle instance methoden anstatt klassen methoden
verkürzen des codes durch ausnutzung der rückgabe werte von procs
nutzen anderer funktionen anstatt die für numeric oder string
und ein wenig optimierung
zb concat anstatt +=""
push(x,y) anstatt mehrere push
x = option['x'] || 0 anstatt x = option['x']; x ||= 0
each_with_index anstatt mit variabeln die NICHT genutzt werden
(und weiteres)
Alles ist wahr, wenn man für wahr einen bestimmten Wert annimmt.
Benutzerinformationen überspringen
Motto: Wer anderen eine Bratwurst brät, der hat ein Bratwurstbratgerät.
Das hab ich absichtlich so gemacht ._.
Zitat
das solltest du schon sehen wie zb APng_File eine tochter von pngfile ist
und die methoden sind alle instance methoden anstatt klassen methoden
Wo benutzt du denn Procs? Oo
Zitat
verkürzen des codes durch ausnutzung der rückgabe werte von procs
Du hast doch jetzt nur die Methodenaufrufe durch den direkten Code ersetzt, oder nicht?
Zitat
nutzen anderer funktionen anstatt die für numeric oder string
Dafür gibts doch Methoden ._.
Hab ich übernommen.
Zitat
push(x,y) anstatt mehrere push
x = option['x'] || 0 anstatt x = option['x']; x ||= 0
Ist nicht gut, das eine mal wo ich String += Anderer String mache, ist bei Bitmap#make_png bzw Bitmap#to_apng und ich will ja nicht einfach den Parameter ändern.
Zitat
concat anstatt +=""
Keine Ahnung, warum ich die beiden Variablen da noch hatte ^^"
Zitat
each_with_index anstatt mit variabeln die NICHT genutzt werden
Zitat
Das hab ich absichtlich so gemacht ._.
ich find da lässt sich streiten, als instanc funktionieren die genauso wie als klasse
und wieso Apng nicht tochter von png?
Zitat
verkürzen des codes durch ausnutzung der rückgabe werte
deins:
|
|
Ruby Quellcode |
1 2 3 4 |
data = nil Zlib::GzipReader.open('hoge.gz') do |gz| data = gz.read end |
meins:
|
|
Ruby Quellcode |
1 |
data = Zlib::GzipReader.open('hoge.gz') { |gz| gz.read } |
mit proc meine ich die blöcke {} die haben wie alles einen rückgabe wert den man wiederum nutzen kann
dec_place, und byte_size
habe ich gegen andere funktion ersetzt weil ich mir dacht das man die nicht unbedingt gut in numeric passt ...
Zitat
ich will ja nicht einfach den Parameter ändern.
Zitat
ok seh ich ein, aber in dem fall muss man abwegen ob es da eine rolle spielt
PS: woran ich morgen arbeite (unteranderem):
make_apng soll auch array von rect als parameter akzeptieren (und vllt noch ein zweites rect)
so kann man ein bitmap in ein apng umwandeln indem es die auschnitte anzeigt
Alles ist wahr, wenn man für wahr einen bestimmten Wert annimmt.
Little-Endian und Big-Endian sind die Bytereihenfolge, also ob du die Bytes von "links" nach "rechts" oder andersherum schreibst. Oder anders: Big-Endian heißt du schreibst das höherwertige Byte zuerst, bei Little-Endian eben das niederwertige Byte zuerst.
Zitat
@Cremno: Wo ist denn der überhaupt der Unterschied zwischen "network byte order" und... dem anderen?
Weil mit S klappts ja auch^^"
Dann ist das imo ein Fehler in deinem Design. Bitmaps gehören nicht gemarshalt.
Zitat
du könntest ja gleich wieder dafür sorgen das Bitmap und weitere instance variabeln gemashalt werden können ...
wenn ein objekt nicht gemashalt werden kann, geht mir das gewaltig auf den Keks ...
Genau das ist das Problem. Es hat schon seinen Sinn warum in der RGSS immer nur der Index eines Items, und nicht die Referenz auf das Item selbst, abgespeichert wird. Man sollte imo schon eine klare Grenze ziehen zwischen eingelesenen, konstanten Daten und Konfigurationen, Ingame-Grafiken und veränderlichen Spielmodellen. Wenn man nur letzteres per Marshal serialisiert und die anderen Dinge jedes Mal neu einliest und aufbaut, kommt man erst gar nicht zu solchen Problemen.
Zitat
auserdem das ich probleme hatte wenn ich meine game items und rpg items gespeichert hatte, waren die rpg items die sich in den game items nich befanden nicht mehr gleich mit den rpg items die ich extra gespeichert hatte (aber das hab ich gefixt)
Zitat
Ist nicht gut, das eine mal wo ich String += Anderer String mache, ist bei Bitmap#make_png bzw Bitmap#to_apng und ich will ja nicht einfach den Parameter ändern.
Sehe ich auch so. Parameter sollte man möglichst nicht modifizieren, es sei denn es kostet wirklich spürbar Performance. Aber das ist hier ja nicht der Fall.
|
|
Ruby Quellcode |
1 2 3 4 5 6 |
class String def byte_size unpack('C*').size end alias_method :byte_length, :byte_size end |
Solange du mit Ruby 1.8 arbeitest sollte String#size dir bereits die Bytelänge eines Strings geben.


