class Rbimg::PNG::Chunk
Attributes
data[R]
length[R]
type[R]
Public Class Methods
IDATs(pixels, color_type: ,bit_depth:, width: , height: , idat_size: 2 ** 20)
click to toggle source
# File lib/image_types/png.rb, line 417 def self.IDATs(pixels, color_type: ,bit_depth:, width: , height: , idat_size: 2 ** 20) case color_type when 0 pixel_width = width when 2 pixel_width = width * 3 when 3 pixel_width = width when 4 pixel_width = width * 2 when 6 pixel_width = width * 4 else raise ArgumentError.new("#{color_type} is not a valid color type. Must be 0,2,3,4, or 6") end expected_pixels = pixel_width * height raise ArgumentError.new("pixel count (#{pixels.length}) does not match expected pixel count (#{expected_pixels})") if pixels.length != expected_pixels pixel_square = Array.new(height, nil) pixel_square = pixel_square.map{ |_| [nil] * pixel_width} for i in 0...pixels.length row = i / pixel_width col = i % pixel_width pixel_square[row][col] = pixels[i] end scanlines = pixel_square.map do |bit_strm| case bit_depth when 1 raise ArgumentError.new("If bit depth is 1, all pixel values must be a 1 or 0") if bit_strm.any?{ |b| b != 0 && b != 1 } bits = bit_strm.join('') + ("0" * ((-1 * bit_strm.length % 8) % 8 )) Byteman.hex(0) + Byteman.pad(num: Byteman.hex(bits.to_i(2)), len: bits.length / 8) when 2 bits = bit_strm.map do |b| raise ArgumentError.new("If bit depth is 2, all pixel values must be between 0 and 3") if !b.between?(0,3) Byteman.pad(num: b, len: 2, type: :bits) end.join('') padded_bits = bits + ("0" * ((-1 * bit_strm.length * 2) % 8) % 8) Byteman.hex(0) + Byteman.pad(num: Byteman.hex(padded_bits.to_i(2)), len: padded_bits.length / 8) when 4 bits = bit_strm.map do |b| raise ArgumentError.new("If bit depth is 4, all pixel values must be between 0 and 15") if !b.between?(0,15) Byteman.pad(num: b, len: 4, type: :bits) end.join('') padded_bits = bits + ("0" * ((-1 * bit_strm.length * 4) % 8) % 8) Byteman.hex(0) + Byteman.pad(num: Byteman.hex(padded_bits.to_i(2)), len: padded_bits.length / 8) when 8 raise ArgumentError.new("If bit depth is 8, all pixel values must be between 0 and 255") if bit_strm.any?{|b| !b.between?(0,255)} ([0] + bit_strm).pack("C*") when 16 raise ArgumentError.new("If bit depth is 16, all pixel values must be between 0 and 65535") if bit_strm.any?{|b| !b.between?(0,65535)} Byteman.hex(0) + bit_strm.map{|b| Byteman.pad(num: Byteman.hex(b), len: 2)}.join('') else ArgumentError.new("bit_depth can only be 1,2,4,8, or 16 bits") end end z = Zlib::Deflate.new(Zlib::BEST_COMPRESSION, Zlib::MAX_WBITS, Zlib::MAX_MEM_LEVEL, Zlib::RLE) zstrm = z.deflate(scanlines.join(''), Zlib::FINISH) z.close idats = [] zstrm.bytes.each_slice(idat_size) do |dstrm| idats << Chunk.new(type: "IDAT", data: dstrm) end idats end
IEND()
click to toggle source
# File lib/image_types/png.rb, line 487 def self.IEND Chunk.new(type: "IEND", data: nil) end
IHDR(width: , height: , bit_depth: , color_type: , compression_method: 0, filter_method: 0, interlace_method: 0)
click to toggle source
# File lib/image_types/png.rb, line 383 def self.IHDR(width: , height: , bit_depth: , color_type: , compression_method: 0, filter_method: 0, interlace_method: 0) bit_depth_rules = { 0 => [1,2,4,8,16], 2 => [8, 16], 3 => [1,2,4,8], 4 => [8,16], 6 => [8,16] } raise ArgumentError.new("Width or height cannot be 0") if width == 0 || height == 0 test_length(width) test_length(height) raise ArgumentError.new('Color code types must be 0, 2, 3, 4, or 6') if ![0,2,3,4,6].include?(color_type) raise ArgumentError.new("Bit depth must be related to color_type as such: color_value => bit_depth_options: #{bit_depth_rules}") if !bit_depth_rules[color_type].include?(bit_depth) wbytes = Byteman.pad(len: 4, num: Byteman.int2buf(width)) hbytes = Byteman.pad(len: 4, num: Byteman.int2buf(height)) bdbyte = [bit_depth] ctbyte = [color_type] cmbyte = [compression_method] fmbyte = [filter_method] ilmbyte = [interlace_method] data = wbytes + hbytes + bdbyte + ctbyte + cmbyte + fmbyte + ilmbyte Chunk.new(type: "IHDR", data: data) end
PLTE(data)
click to toggle source
# File lib/image_types/png.rb, line 410 def self.PLTE(data) data = data.bytes if data.is_a?(String) raise ArgumentError.new("Number of bytes must be a multiple of 3") if data.length % 3 != 0 raise ArgumentError.new("Pallette length must be between 1 and 256") if data.length < 3 || data.length > (256 * 3) Chunk.new(type: "PLTE", data: data) end
crc_valid?(type:, data:, crc:)
click to toggle source
# File lib/image_types/png.rb, line 378 def self.crc_valid?(type:, data:, crc:) c = new(type: type, data: data) c.bytes[-4..-1].bytes == crc end
new(type: ,data:)
click to toggle source
# File lib/image_types/png.rb, line 494 def initialize(type: ,data:) raise ArgumentError.new("Type must be a string, symbol, or an array") if !type.is_a?(String) && !type.is_a?(Symbol) && !type.is_a?(Array) @data = data.nil? ? [] : data @length = Byteman.pad(len: 4, num: Byteman.int2buf(@data.length)) self.class.test_length(@data.length) if type.is_a?(String) || type.is_a?(Symbol) @type = type.to_s.bytes else @type = type end @crc_strategy = Rbimg::Strategies::CRCTableLookup @crc = crc end
readChunk(bytes)
click to toggle source
# File lib/image_types/png.rb, line 342 def self.readChunk(bytes) bytes = bytes.bytes if bytes.is_a?(String) data = {} data[:length] = Byteman.buf2int(bytes[0...4]) data[:type] = Byteman.buf2hex(bytes[4...8]) data[:crc] = bytes[-4..-1] data[:chunk_data] = bytes[8...(8 + data[:length])] raise Rbimg::CRCError.new("CRC does not match expected") if !crc_valid?(type: data[:type].unpack("C*"), data: data[:chunk_data], crc: data[:crc]) data end
readIDAT(bytes)
click to toggle source
# File lib/image_types/png.rb, line 368 def self.readIDAT(bytes) data = readChunk(bytes) data[:compressed_pixels] = data[:chunk_data] data end
readIHDR(bytes)
click to toggle source
# File lib/image_types/png.rb, line 353 def self.readIHDR(bytes) bytes = bytes.bytes if bytes.is_a?(String) raise ArgumentError.new("IHDR must be 25 bytes long") if bytes.length != 25 data = readChunk(bytes) data[:width] = Byteman.buf2int(bytes[8...12]) data[:height] = Byteman.buf2int(bytes[12...16]) data[:bit_depth] = bytes[16] data[:color_type] = bytes[17] data[:compression_method] = bytes[18] data[:filter_method] = bytes[19] data[:interlace_method] = bytes[20] data end
readPLTE(bytes)
click to toggle source
# File lib/image_types/png.rb, line 374 def self.readPLTE(bytes) readChunk(bytes) end
Private Class Methods
test_length(num, limit: 2 ** 31 - 1)
click to toggle source
# File lib/image_types/png.rb, line 520 def self.test_length(num, limit: 2 ** 31 - 1) raise ArgumentError.new("Length cannot exceed 2^31 - 1") if num > limit end
Public Instance Methods
bytes()
click to toggle source
# File lib/image_types/png.rb, line 513 def bytes data.pack("C*") end
Private Instance Methods
crc()
click to toggle source
# File lib/image_types/png.rb, line 525 def crc @crc_strategy.crc(@type, @data) end