Parent

Prawn::Images::PNG

A convenience class that wraps the logic for extracting the parts of a PNG image that we need to embed them in a PDF

Attributes

palette[R]
img_data[R]
transparency[R]
width[R]
height[R]
bits[R]
color_type[R]
compression_method[R]
filter_method[R]
interlace_method[R]
alpha_channel[R]
scaled_width[RW]
scaled_height[RW]

Public Class Methods

new(data) click to toggle source

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

Public Instance Methods

colors() click to toggle source

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
pixel_bitlength() click to toggle source

number of bits used per pixel

     # File lib/prawn/images/png.rb, line 106
106:       def pixel_bitlength
107:         if alpha_channel?
108:           self.bits * (self.colors + 1)
109:         else
110:           self.bits * self.colors
111:         end
112:       end
split_alpha_channel!() click to toggle source

split the alpha channel data from the raw image data in images where it’s required.

     # File lib/prawn/images/png.rb, line 117
117:       def split_alpha_channel!
118:         unfilter_image_data if alpha_channel?
119:       end

Private Instance Methods

alpha_channel?() click to toggle source
     # File lib/prawn/images/png.rb, line 123
123:       def alpha_channel?
124:         @color_type == 4 || @color_type == 6
125:       end
unfilter_image_data() click to toggle source
     # 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.

[Validate]

Generated with the Darkfish Rdoc Generator 1.1.6.