Class: Pruim::BMP

Inherits:
Object
  • Object
show all
Includes:
Codec
Defined in:
lib/pruim/bmp.rb

Defined Under Namespace

Classes: BGR, BGRX, ColorTableRGBX, CoreHeader, ExtraHeader, Header

Constant Summary collapse

BI_RGB =
0
BI_RLE8 =

RLE 8-bit/pixel Can be used only with 8-bit/pixel bitmaps

1
BI_RLE4 =

RLE 4-bit/pixel Can be used only with 4-bit/pixel bitmaps

2
BI_BITFIELDS =

Bit field or Huffman 1D compression for BITMAPCOREHEADER2

3
BI_JPEG =

JPEG or RLE-24 compression for BITMAPCOREHEADER2

4
BI_PNG =

PNG The bitmap contains a PNG image.

5
BITMAPINFOHEADER =

Commonly used

40
BITMAPV5HEADER =

Latest version

124
HEADER_SIZE =
14

Instance Method Summary collapse

Methods included from Codec

#can_encode?, #encode_will_degrade?, for_filename, for_filename_name, for_name, new_for_filename, new_for_filename_name, new_for_name, register, #text

Instance Method Details

#calc_padding(wide, palette = true) ⇒ Object

Calculate padding size



134
135
136
137
138
139
# File 'lib/pruim/bmp.rb', line 134

def calc_padding(wide, palette = true)
  bs      = palette ? 1 : 3
  padding = (1 * bs * wide) % 4;
  padding = 4 - padding if padding != 0
  return padding
end

#can_decode?(io) ⇒ Boolean

Returns:

  • (Boolean)


98
99
100
101
102
# File 'lib/pruim/bmp.rb', line 98

def can_decode?(io)
  header = Header.read(io)
  io.rewind
  return header && header.magic == 'BM'
end

#decode(io) ⇒ Object



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/pruim/bmp.rb', line 162

def decode(io)
  header = Header.read(io)
  core   = CoreHeader.read(io)
  extra  = nil
  if (core.header_size == BITMAPINFOHEADER)
    extra = ExtraHeader.read(io)
  end
  io.seek(HEADER_SIZE + core.header_size)
  # skip rest of header that we don't support
  if core.bitspp == 8
    return decode_bpp8(io, header, core, extra)
  end
  # TODO: real color bitmaps.
  return nil
end

#decode_bpp8(io, header, core, extra) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/pruim/bmp.rb', line 142

def decode_bpp8(io, header, core, extra)
  # p header, core, extra
  io.seek(HEADER_SIZE + core.header_size)      
  palette = read_palette(io, header, core, extra)
  # Skip to the bitmap, gap may be there...
  io.seek(header.bitmap_offset)
  # Calculate padding size
  padding = calc_padding(core.width, true)
  data    = nil
  case extra.compress_type
    when BI_RGB
      data = decode_bpp8_rgb(io, header, core, extra, padding)
    else
      return nil
  end
  image   = Image.new(core.width, core.height, :depth => core.bitspp, 
                    :palette => palette, :pages => 1, :data => [data])
  return image
end

#decode_bpp8_rgb(io, header, core, extra, padding) ⇒ Object



117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# File 'lib/pruim/bmp.rb', line 117

def decode_bpp8_rgb(io, header, core, extra, padding)
  data = []
  for ypos in (0...core.height) 
    size = core.width + padding
    # read a line with padding
    raise "End of file when reading BMP btyes!" if io.eof?
    str  = io.read(size)
    raise "Short read when reading BMP bytes!" unless str && str.bytesize == size
    # make an array out of it and drop the padding
    arr  = str.bytes.to_a
    arr.pop(padding)
    data = arr + data # prepend data
  end      
  return data
end

#encode(image, io) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'lib/pruim/bmp.rb', line 207

def encode(image, io)
  bitcount    = (image.palette? ? 8 : 24)
  bitmap_size = ((image.w * bitcount) / 8) * image.h
  info_size   = BITMAPINFOHEADER
  # Data for the palette
  if image.palette?
    info_size += image.palette.size * 4
  end
  total_size = 14 + info_size + bitmap_size
  # write bmp header info 
  header = Header.new.set('BM', total_size, 0, 0, 14 + info_size)
  header.write(io)
  core   = CoreHeader.new.set(BITMAPINFOHEADER, image.w, image.h, 1, bitcount)
  core.write(io)
  extra  = ExtraHeader.new.set(BI_RGB, 0, 0, bitcount, image.palette.size, 0)
  extra.write(io)
  # Calculate padding size
  padding = calc_padding(image.active.w, image.palette?)
  if image.palette?
    encode_bpp8(image, io, padding)
  else
    encode_bpp24(image, io, padding)
  end
  return image
end

#encode_bpp24(image, io, padding) ⇒ Object



204
205
# File 'lib/pruim/bmp.rb', line 204

def encode_bpp24(image, io, padding)
end

#encode_bpp8(image, io, padding) ⇒ Object



199
200
201
202
# File 'lib/pruim/bmp.rb', line 199

def encode_bpp8(image, io, padding)
  encode_palette(image, io)
  encode_bpp8_rgb(image, io, padding)
end

#encode_bpp8_rgb(image, io, padding) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
# File 'lib/pruim/bmp.rb', line 186

def encode_bpp8_rgb(image, io, padding)
  page = image.active
  ypos = image.h - 1
  while ypos >= 0
    row   = page.row(ypos)
    row   = row + [0] * padding
    str   = row.pack('C*')
    io.write(str)
    ypos -= 1
  end
end

#encode_palette(image, io) ⇒ Object



178
179
180
181
182
183
184
# File 'lib/pruim/bmp.rb', line 178

def encode_palette(image, io)
  image.palette.each do |color|
    r, g, b = *Color.to_rgb(color)
    bgrx    = BGRX.new.set(b, g, r, 0)
    bgrx.write(io)
  end
end

#read_palette(io, header, core, extra) ⇒ Object



105
106
107
108
109
110
111
112
113
114
# File 'lib/pruim/bmp.rb', line 105

def read_palette(io, header, core, extra)
  palette = Pruim::Palette.new
  ncolors = 2 ** core.bitspp
  for i in 0..255
    color = BGRX.read(io)
    palette.new_rgb(color.r, color.g, color.b)
    raise "Unexpected end of file whilst reading bmp palette!" if io.eof?
  end
  return palette
end