module Origami::Filter::Predictor
Constants
- NONE
- PNG_AVERAGE
- PNG_NONE
- PNG_OPTIMUM
- PNG_PAETH
- PNG_SUB
- PNG_UP
- TIFF
Public Class Methods
included(receiver)
click to toggle source
# File lib/origami/filters/predictors.rb, line 48 def self.included(receiver) raise TypeError, "Predictors only applies to Filters" unless receiver.include?(Filter) end
new(parameters = {})
click to toggle source
Create a new predictive Filter
.
- parameters
-
A hash of filter options.
Calls superclass method
# File lib/origami/filters/predictors.rb, line 56 def initialize(parameters = {}) super(DecodeParms.new(parameters)) end
Private Instance Methods
apply_post_prediction(data, predictor: NONE, colors: 1, bpc: 8, columns: 1)
click to toggle source
# File lib/origami/filters/predictors.rb, line 102 def apply_post_prediction(data, predictor: NONE, colors: 1, bpc: 8, columns: 1) return data if predictor == NONE bpp, bpr = compute_bpp_bpr(data, columns, colors, bpc) if predictor == TIFF tiff_post_prediction(data, colors, bpc, columns) elsif predictor >= 10 # PNG # Each line has an extra predictor byte. png_post_prediction(data, bpp, bpr + 1) else raise PredictorError.new("Unknown predictor : #{predictor}", input_data: data) end end
apply_pre_prediction(data, predictor: NONE, colors: 1, bpc: 8, columns: 1)
click to toggle source
# File lib/origami/filters/predictors.rb, line 83 def apply_pre_prediction(data, predictor: NONE, colors: 1, bpc: 8, columns: 1) return data if predictor == NONE bpp, bpr = compute_bpp_bpr(data, columns, colors, bpc) unless data.size % bpr == 0 raise PredictorError.new("Invalid data size #{data.size}, should be multiple of bpr=#{bpr}", input_data: data) end if predictor == TIFF tiff_pre_prediction(data, colors, bpc, columns) elsif predictor >= 10 # PNG png_pre_prediction(data, predictor, bpp, bpr) else raise PredictorError.new("Unknown predictor : #{predictor}", input_data: data) end end
compute_bpp_bpr(data, columns, colors, bpc)
click to toggle source
Computes the number of bytes per pixel and number of bytes per row.
# File lib/origami/filters/predictors.rb, line 120 def compute_bpp_bpr(data, columns, colors, bpc) unless (1..4) === colors raise PredictorError.new("Colors must be between 1 and 4", input_data: data) end unless [1,2,4,8,16].include?(bpc) raise PredictorError.new("BitsPerComponent must be in 1, 2, 4, 8 or 16", input_data: data) end # components per line nvals = columns * colors # bytes per pixel bpp = (colors * bpc + 7) >> 3 # bytes per row bpr = (nvals * bpc + 7) >> 3 [ bpp, bpr ] end
png_apply_prediction(predictor, value, up, left, upleft) { |value, left| ... }
click to toggle source
Computes the next component value given a predictor and adjacent components. A block must be passed to apply the operation.
# File lib/origami/filters/predictors.rb, line 222 def png_apply_prediction(predictor, value, up, left, upleft) result = case predictor when PNG_NONE value when PNG_SUB yield(value, left) when PNG_UP yield(value, up) when PNG_AVERAGE yield(value, (left + up) / 2) when PNG_PAETH yield(value, png_paeth_choose(up, left, upleft)) else raise PredictorError, "Unsupported PNG predictor : #{predictor}" end (result & 0xFF).chr end
png_paeth_choose(left, up, upleft)
click to toggle source
Choose the preferred value in a PNG paeth predictor given the left, up and up left samples.
# File lib/origami/filters/predictors.rb, line 246 def png_paeth_choose(left, up, upleft) p = left + up - upleft pa, pb, pc = (p - left).abs, (p - up).abs, (p - upleft).abs case [pa, pb, pc].min when pa then left when pb then up when pc then upleft end end
png_post_prediction(data, bpp, bpr)
click to toggle source
Decodes the PNG input data. Each line should be prepended by a byte identifying a PNG predictor.
# File lib/origami/filters/predictors.rb, line 145 def png_post_prediction(data, bpp, bpr) result = "" uprow = "\0" * bpr thisrow = "\0" * bpr nrows = (data.size + bpr - 1) / bpr nrows.times do |irow| line = data[irow * bpr, bpr] predictor = 10 + line[0].ord line[0] = "\0" for i in (1..line.size-1) up = uprow[i].ord if bpp > i left = upleft = 0 else left = line[i - bpp].ord upleft = uprow[i - bpp].ord end begin thisrow[i] = png_apply_prediction(predictor, line[i].ord, up, left, upleft, &:+) rescue PredictorError => error thisrow[i] = line[i] if Origami::OPTIONS[:ignore_png_errors] error.input_data = data error.decoded_data = result raise(error) end end result << thisrow[1..-1] uprow = thisrow end result end
png_pre_prediction(data, predictor, bpp, bpr)
click to toggle source
Encodes the input data given a PNG predictor.
# File lib/origami/filters/predictors.rb, line 187 def png_pre_prediction(data, predictor, bpp, bpr) result = "" nrows = data.size / bpr line = "\0" + data[-bpr, bpr] (nrows - 1).downto(0) do |irow| uprow = if irow == 0 "\0" * (bpr + 1) else "\0" + data[(irow - 1) * bpr, bpr] end bpr.downto(1) do |i| up = uprow[i].ord left = line[i - bpp].ord upleft = uprow[i - bpp].ord line[i] = png_apply_prediction(predictor, line[i].ord, up, left, upleft, &:-) end line[0] = (predictor - 10).chr result = line + result line = uprow end result end
post_prediction(data)
click to toggle source
# File lib/origami/filters/predictors.rb, line 68 def post_prediction(data) return data unless @params.Predictor.is_a?(Integer) apply_post_prediction(data, prediction_parameters) end
pre_prediction(data)
click to toggle source
# File lib/origami/filters/predictors.rb, line 62 def pre_prediction(data) return data unless @params.Predictor.is_a?(Integer) apply_pre_prediction(data, prediction_parameters) end
prediction_parameters()
click to toggle source
# File lib/origami/filters/predictors.rb, line 74 def prediction_parameters { predictor: @params.Predictor.to_i, colors: @params.Colors.is_a?(Integer) ? @params.Colors.to_i : 1, bpc: @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8, columns: @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1, } end