module HexaPDF::Filter::Predictor
def self.decoder(source, options)
def self.decoder(source, options) execute(:decoder, source, options) end
def self.encoder(source, options)
def self.encoder(source, options) execute(:encoder, source, options) end
def self.execute(type, source, options) # :nodoc:
def self.execute(type, source, options) # :nodoc: return source if !options[:Predictor] || options[:Predictor] == 1 colors = options[:Colors] || 1 bits_per_component = options[:BitsPerComponent] || 8 columns = options[:Columns] || 1 if options[:Predictor] == 2 tiff_execute(type, source, colors, bits_per_component, columns) elsif options[:Predictor] >= 10 png_execute(type, source, options[:Predictor], colors, bits_per_component, columns) else raise HexaPDF::InvalidPDFObjectError, "Predictor key is invalid: #{options[:Predictor]}" end end
def self.png_execute(type, source, predictor, colors, bits_per_component, columns) # :nodoc:
def self.png_execute(type, source, predictor, colors, bits_per_component, columns) # :nodoc: Fiber.new do bytes_per_pixel = (bits_per_component * colors + 7) / 8 bytes_per_row = (columns * bits_per_component * colors + 7) / 8 bytes_per_row += 1 if type == :decoder # Only on encoding: Arbitrarily choose a predictor if we should choose the optimum predictor = predictor == 15 ? PREDICTOR_PNG_PAETH : predictor - 10 data = ''.b last_line = "\0".b * (bytes_per_row + 1) pos = 0 decode_row = lambda do |result| line = data[pos + 1, bytes_per_row - 1] case data.getbyte(pos) when PREDICTOR_PNG_SUB bytes_per_pixel.upto(bytes_per_row - 2) do |i| line.setbyte(i, (line.getbyte(i) + line.getbyte(i - bytes_per_pixel)) % 256) end when PREDICTOR_PNG_UP 0.upto(bytes_per_row - 2) do |i| line.setbyte(i, (line.getbyte(i) + last_line.getbyte(i)) % 256) end when PREDICTOR_PNG_AVERAGE 0.upto(bytes_per_row - 2) do |i| a = i < bytes_per_pixel ? 0 : line.getbyte(i - bytes_per_pixel) line.setbyte(i, (line.getbyte(i) + ((a + last_line.getbyte(i)) >> 1)) % 256) end when PREDICTOR_PNG_PAETH 0.upto(bytes_per_row - 2) do |i| a = i < bytes_per_pixel ? 0 : line.getbyte(i - bytes_per_pixel) b = last_line.getbyte(i) c = i < bytes_per_pixel ? 0 : last_line.getbyte(i - bytes_per_pixel) point = a + b - c pa = (point - a).abs pb = (point - b).abs pc = (point - c).abs point = ((pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c)) line.setbyte(i, (line.getbyte(i) + point) % 256) end end result << line last_line = line end encode_row = lambda do |result| line = predictor.chr.force_encoding(Encoding::BINARY) << data[pos, bytes_per_row] next_last_line = line.dup case predictor when PREDICTOR_PNG_SUB bytes_per_row.downto(bytes_per_pixel + 1) do |i| line.setbyte(i, (line.getbyte(i) - line.getbyte(i - bytes_per_pixel)) % 256) end when PREDICTOR_PNG_UP bytes_per_row.downto(1) do |i| line.setbyte(i, (line.getbyte(i) - last_line.getbyte(i)) % 256) end when PREDICTOR_PNG_AVERAGE bytes_per_row.downto(1) do |i| a = i <= bytes_per_pixel ? 0 : line.getbyte(i - bytes_per_pixel) line.setbyte(i, (line.getbyte(i) - ((a + last_line.getbyte(i)) >> 1)) % 256) end when PREDICTOR_PNG_PAETH bytes_per_row.downto(1) do |i| a = i <= bytes_per_pixel ? 0 : line.getbyte(i - bytes_per_pixel) b = last_line.getbyte(i) c = i <= bytes_per_pixel ? 0 : last_line.getbyte(i - bytes_per_pixel) point = a + b - c pa = (point - a).abs pb = (point - b).abs pc = (point - c).abs point = ((pa <= pb && pa <= pc) ? a : (pb <= pc ? b : c)) line.setbyte(i, (line.getbyte(i) - point) % 256) end end result << line last_line = next_last_line end row_action = (type == :decoder ? decode_row : encode_row) while source.alive? && (new_data = source.resume) data.slice!(0...pos) data << new_data result = ''.b pos = 0 while pos + bytes_per_row <= data.length row_action.call(result) pos += bytes_per_row end Fiber.yield(result) unless result.empty? end if pos != data.length && GlobalConfiguration['filter.predictor.strict'] raise FilterError, "Data is missing for PNG predictor" elsif pos != data.length && data.length != 1 result = ''.b bytes_per_row = data.length - pos row_action.call(result) result end end end
def self.tiff_execute(type, source, colors, bits_per_component, columns) # :nodoc:
def self.tiff_execute(type, source, colors, bits_per_component, columns) # :nodoc: Fiber.new do bytes_per_row = (columns * bits_per_component * colors + 7) / 8 mask = (1 << bits_per_component) - 1 data = ''.b writer = HexaPDF::Utils::BitStreamWriter.new pos = 0 decode_row = lambda do |result, reader| last_components = [0] * colors (columns * colors).times do |i| i %= colors tmp = (reader.read(bits_per_component) + last_components[i]) & mask result << writer.write(tmp, bits_per_component) last_components[i] = tmp end result << writer.finalize end encode_row = lambda do |result, reader| last_components = [0] * colors (columns * colors).times do |i| i %= colors tmp = reader.read(bits_per_component) result << writer.write((tmp - last_components[i]) & mask, bits_per_component) last_components[i] = tmp end result << writer.finalize end row_action = (type == :decoder ? decode_row : encode_row) while source.alive? && (new_data = source.resume) data.slice!(0...pos) data << new_data result = ''.b pos = 0 while pos + bytes_per_row <= data.length reader = HexaPDF::Utils::BitStreamReader.new(data[pos, bytes_per_row]) row_action.call(result, reader) pos += bytes_per_row end Fiber.yield(result) unless result.empty? end unless pos == data.length raise FilterError, "Data is missing for TIFF predictor" end end end