Object
A convenience class that wraps the logic for extracting the parts of a PNG image that we need to embed them in a PDF
Process a new PNG image
data | A binary string of PNG data |
# File lib/prawn/images/png.rb, line 30 30: def initialize(data) 31: data = StringIO.new(data.dup) 32: 33: data.read(8) # Skip the default header 34: 35: @palette = "" 36: @img_data = "" 37: @transparency = {} 38: 39: loop do 40: chunk_size = data.read(4).unpack("N")[0] 41: section = data.read(4) 42: case section 43: when 'IHDR' 44: # we can grab other interesting values from here (like width, 45: # height, etc) 46: values = data.read(chunk_size).unpack("NNCCCCC") 47: 48: @width = values[0] 49: @height = values[1] 50: @bits = values[2] 51: @color_type = values[3] 52: @compression_method = values[4] 53: @filter_method = values[5] 54: @interlace_method = values[6] 55: when 'PLTE' 56: @palette << data.read(chunk_size) 57: when 'IDAT' 58: @img_data << data.read(chunk_size) 59: when 'tRNS' 60: # This chunk can only occur once and it must occur after the 61: # PLTE chunk and before the IDAT chunk 62: @transparency = {} 63: case @color_type 64: when 3 65: # Indexed colour, RGB. Each byte in this chunk is an alpha for 66: # the palette index in the PLTE ("palette") chunk up until the 67: # last non-opaque entry. Set up an array, stretching over all 68: # palette entries which will be 0 (opaque) or 1 (transparent). 69: @transparency[:indexed] = data.read(chunk_size).unpack("C*") 70: short = 255 - @transparency[:indexed].size 71: @transparency[:indexed] += ([255] * short) if short > 0 72: when 0 73: # Greyscale. Corresponding to entries in the PLTE chunk. 74: # Grey is two bytes, range 0 .. (2 ^ bit-depth) - 1 75: grayval = data.read(chunk_size).unpack("n").first 76: @transparency[:grayscale] = grayval 77: when 2 78: # True colour with proper alpha channel. 79: @transparency[:rgb] = data.read(chunk_size).unpack("nnn") 80: end 81: when 'IEND' 82: # we've got everything we need, exit the loop 83: break 84: else 85: # unknown (or un-important) section, skip over it 86: data.seek(data.pos + chunk_size) 87: end 88: 89: data.read(4) # Skip the CRC 90: end 91: end
number of color components to each pixel
# File lib/prawn/images/png.rb, line 95 95: def colors 96: case self.color_type 97: when 0, 3, 4 98: return 1 99: when 2, 6 100: return 3 101: end 102: end
# File lib/prawn/images/png.rb, line 123 123: def alpha_channel? 124: @color_type == 4 || @color_type == 6 125: end
# File lib/prawn/images/png.rb, line 127 127: def unfilter_image_data 128: data = Zlib::Inflate.inflate(@img_data).unpack 'C*' 129: @img_data = "" 130: @alpha_channel = "" 131: 132: pixel_bytes = pixel_bitlength / 8 133: scanline_length = pixel_bytes * self.width + 1 134: row = 0 135: pixels = [] 136: paeth, pa, pb, pc = nil 137: until data.empty? do 138: row_data = data.slice! 0, scanline_length 139: filter = row_data.shift 140: case filter 141: when 0 # None 142: when 1 # Sub 143: row_data.each_with_index do |byte, index| 144: left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes] 145: row_data[index] = (byte + left) % 256 146: #p [byte, left, row_data[index]] 147: end 148: when 2 # Up 149: row_data.each_with_index do |byte, index| 150: col = index / pixel_bytes 151: upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes] 152: row_data[index] = (upper + byte) % 256 153: end 154: when 3 # Average 155: row_data.each_with_index do |byte, index| 156: col = index / pixel_bytes 157: upper = row == 0 ? 0 : pixels[row-1][col][index % pixel_bytes] 158: left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes] 159: 160: row_data[index] = (byte + ((left + upper)/2).floor) % 256 161: end 162: when 4 # Paeth 163: left = upper = upper_left = nil 164: row_data.each_with_index do |byte, index| 165: col = index / pixel_bytes 166: 167: left = index < pixel_bytes ? 0 : row_data[index - pixel_bytes] 168: if row.zero? 169: upper = upper_left = 0 170: else 171: upper = pixels[row-1][col][index % pixel_bytes] 172: upper_left = col.zero? ? 0 : 173: pixels[row-1][col-1][index % pixel_bytes] 174: end 175: 176: p = left + upper - upper_left 177: pa = (p - left).abs 178: pb = (p - upper).abs 179: pc = (p - upper_left).abs 180: 181: paeth = if pa <= pb && pa <= pc 182: left 183: elsif pb <= pc 184: upper 185: else 186: upper_left 187: end 188: 189: row_data[index] = (byte + paeth) % 256 190: end 191: else 192: raise ArgumentError, "Invalid filter algorithm #{filter}" 193: end 194: 195: s = [] 196: row_data.each_slice pixel_bytes do |slice| 197: s << slice 198: end 199: pixels << s 200: row += 1 201: end 202: 203: # convert the pixel data to seperate strings for colours and alpha 204: color_byte_size = self.colors * self.bits / 8 205: alpha_byte_size = self.bits / 8 206: pixels.each do |this_row| 207: this_row.each do |pixel| 208: @img_data << pixel[0, color_byte_size].pack("C*") 209: @alpha_channel << pixel[color_byte_size, alpha_byte_size].pack("C*") 210: end 211: end 212: 213: # compress the data 214: @img_data = Zlib::Deflate.deflate(@img_data) 215: @alpha_channel = Zlib::Deflate.deflate(@alpha_channel) 216: end
Disabled; run with --debug to generate this.
Generated with the Darkfish Rdoc Generator 1.1.6.