Class: FormatParser::BMPParser

Inherits:
Object
  • Object
show all
Includes:
IOUtils
Defined in:
lib/parsers/bmp_parser.rb

Overview

Constant Summary collapse

VALID_BMP =
'BM'
PERMISSIBLE_PIXEL_ARRAY_LOCATIONS =
26..512
BMP_MIME_TYPE =
'image/bmp'

Constants included from IOUtils

IOUtils::INTEGER_DIRECTIVES

Instance Method Summary collapse

Methods included from IOUtils

#read_bytes, #read_fixed_point, #read_int, #safe_read, #safe_skip, #skip_bytes

Instance Method Details

#call(io) ⇒ Object



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/parsers/bmp_parser.rb', line 14

def call(io)
  io = FormatParser::IOConstraint.new(io)

  magic_number, _file_size, _reserved1, _reserved2, pix_array_location = safe_read(io, 14).unpack('A2Vv2V')
  return unless VALID_BMP == magic_number

  # The number that gets unpacked can be fairly large, but in practice this offset cannot be too big -
  # the DIB image header won't be that big anyway/
  return unless PERMISSIBLE_PIXEL_ARRAY_LOCATIONS.cover?(pix_array_location)

  dib_header = safe_read(io, 40)
  header_size = dib_header.unpack('V')[0]
  case header_size
  when 12 # OS21XBITMAPHEADER
    parse_bitmap_core_header(dib_header)
  else # More modern implementations
    parse_modern_header(dib_header)
  end
end

#likely_match?(filename) ⇒ Boolean

Returns:

  • (Boolean)


10
11
12
# File 'lib/parsers/bmp_parser.rb', line 10

def likely_match?(filename)
  filename =~ /\.bmp$/i
end

#parse_bitmap_core_header(dib_header) ⇒ Object



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/parsers/bmp_parser.rb', line 34

def parse_bitmap_core_header(dib_header)
  _header_size, width, height, _num_color_planes, bit_depth = dib_header.unpack('VSSSS')

  # In core bitmap format an unsigned int is used for dimensions,
  # no inverse scan order is possible
  data_order = :normal

  FormatParser::Image.new(
    format: :bmp,
    width_px: width,
    height_px: height,
    color_mode: :rgb,
    content_type: BMP_MIME_TYPE,
    intrinsics: {
      data_order: data_order,
      bits_per_pixel: bit_depth
    }
  )
end

#parse_modern_header(dib_header) ⇒ Object



54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/parsers/bmp_parser.rb', line 54

def parse_modern_header(dib_header)
  _header_size, width, height, _planes, bits_per_pixel,
  _compression_method, _image_size, horizontal_res,
  vertical_res, _n_colors, _i_colors = dib_header.unpack('Vl<2v2V2l<2V2')

  # There are cases where the height might by negative indicating the data
  # is ordered from top to bottom instead of bottom to top
  data_order = height < 0 ? :inverse : :normal

  FormatParser::Image.new(
    format: :bmp,
    width_px: width,
    height_px: height.abs,
    color_mode: :rgb,
    content_type: BMP_MIME_TYPE,
    intrinsics: {
      vertical_resolution: vertical_res,
      horizontal_resolution: horizontal_res,
      data_order: data_order,
      bits_per_pixel: bits_per_pixel,
    }
  )
end