Class: Pura::Image::Processor

Inherits:
Object
  • Object
show all
Defined in:
lib/pura/image/processor.rb

Constant Summary collapse

MAGIC_BYTES =
{
  jpeg: [0xFF, 0xD8],
  png: [0x89, 0x50, 0x4E, 0x47],
  gif: [0x47, 0x49, 0x46],           # "GIF"
  bmp: [0x42, 0x4D],                 # "BM"
  tiff_le: [0x49, 0x49, 0x2A, 0x00], # "II*\0" little-endian
  tiff_be: [0x4D, 0x4D, 0x00, 0x2A], # "MM\0*" big-endian
  webp: [0x52, 0x49, 0x46, 0x46],    # "RIFF" (+ "WEBP" at offset 8)
  ico: [0x00, 0x00, 0x01, 0x00],     # ICO
  cur: [0x00, 0x00, 0x02, 0x00]      # CUR
}.freeze
EXTENSION_MAP =
{
  ".jpg" => :jpeg, ".jpeg" => :jpeg,
  ".png" => :png,
  ".bmp" => :bmp,
  ".gif" => :gif,
  ".tif" => :tiff, ".tiff" => :tiff,
  ".webp" => :webp,
  ".ico" => :ico,
  ".cur" => :ico
}.freeze

Class Method Summary collapse

Class Method Details

.convert(input_path, output_path, **options) ⇒ Object



65
66
67
68
# File 'lib/pura/image/processor.rb', line 65

def convert(input_path, output_path, **options)
  image = load(input_path)
  save(image, output_path, **options)
end

.detect_format(data) ⇒ Object

Raises:

  • (ArgumentError)


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/pura/image/processor.rb', line 30

def detect_format(data)
  bytes = data.bytes

  return :jpeg if bytes[0] == 0xFF && bytes[1] == 0xD8
  return :png if bytes[0..3] == MAGIC_BYTES[:png]
  return :gif if bytes[0..2] == MAGIC_BYTES[:gif]
  return :bmp if bytes[0..1] == MAGIC_BYTES[:bmp]
  return :tiff if bytes[0..3] == MAGIC_BYTES[:tiff_le] || bytes[0..3] == MAGIC_BYTES[:tiff_be]
  return :ico if bytes[0..3] == MAGIC_BYTES[:ico] || bytes[0..3] == MAGIC_BYTES[:cur]

  if bytes[0..3] == MAGIC_BYTES[:webp] && bytes.length >= 12 && (bytes[8..11] == [0x57, 0x45, 0x42, 0x50])
    return :webp # "WEBP"
  end

  raise ArgumentError, "Unsupported image format"
end

.detect_format_by_extension(path) ⇒ Object



47
48
49
50
# File 'lib/pura/image/processor.rb', line 47

def detect_format_by_extension(path)
  ext = File.extname(path).downcase
  EXTENSION_MAP[ext] || raise(ArgumentError, "Unsupported file extension: #{ext}")
end

.load(path) ⇒ Object



52
53
54
55
56
57
# File 'lib/pura/image/processor.rb', line 52

def load(path)
  data = File.binread(path, 16)
  format = detect_format(data)
  image = decode_with_format(path, format)
  wrap(image)
end

.save(image, path, **options) ⇒ Object



59
60
61
62
63
# File 'lib/pura/image/processor.rb', line 59

def save(image, path, **options)
  format = detect_format_by_extension(path)
  raw_image = unwrap(image)
  encode_with_format(raw_image, path, format, **options)
end

.unwrap(wrapper) ⇒ Object



74
75
76
# File 'lib/pura/image/processor.rb', line 74

def unwrap(wrapper)
  Pura::Jpeg::Image.new(wrapper.width, wrapper.height, wrapper.pixels)
end

.wrap(raw_image) ⇒ Object



70
71
72
# File 'lib/pura/image/processor.rb', line 70

def wrap(raw_image)
  Wrapper.new(raw_image.width, raw_image.height, raw_image.pixels.dup)
end