#!/usr/bin/env ruby # Copyright 2024 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'RMagick' im = Magick::ImageList.new(ARGV[0]).first current_data = 0 current_alpha = 0 data = [] alpha = [] 0.upto(im.rows - 1) do |r| 0.upto(maxcol = (im.columns - 1)) do |c| pix = im.pixel_color(c, r) if pix.intensity == 0 # black current_data |= (1 << (c % 8)) end if pix.opacity == 0 # opaque current_alpha |= (1 << (c % 8)) end if c % 8 == 7 or c == maxcol data << current_data alpha << current_alpha current_data = 0 current_alpha = 0 end end end packed_data, packed_alpha = [data, alpha].map do |raw| i = 0 packet_start = 0 out = [] while i < raw.size if i < raw.size - 1 and raw[i] == raw[i+1] # output literal packet if i > packet_start out << (i - packet_start - 1) out += raw[packet_start..i-1] end # start a run packet_start = i while raw[i] == raw[i+1] and (i - packet_start) < 127 i += 1 end out << -(i - packet_start) out << raw[i] # next packet starts at next byte packet_start = i + 1 elsif (i - packet_start) == 127 or i == raw.size - 1 # too many literal bytes, output and move to next packet out << (i - packet_start) out += raw[packet_start..i] packet_start = i + 1 end i += 1 end unpacked_size = 0 i = 0 while i < out.size if out[i] > 0 unpacked_size += out[i] + 1 i += out[i] + 2 else unpacked_size += 1 - out[i] i += 2 end end #puts unpacked_size #puts out.size out end ARGV[0] =~ /(.*)\..*$/ file_name = "#{$1}.packbits" puts "Wrote #{file_name}" puts "Compressed image size is: " + packed_data.size.to_s File.open(file_name, 'w') do |file| file.print 'PBPB' file.print [im.columns, im.rows].pack('n*') file.print [packed_data.size].pack('N*') file.print packed_data.pack('c*') end