realterrain/lib/loader_bmp.lua
2015-12-06 20:56:06 -08:00

252 lines
5.7 KiB
Lua

-- Original file is part of Allegro 4.4.
-- BMP loader by Seymour Shlien.
-- OS/2 BMP support and BMP save function by Jonas Petersen.
-- Translated to Lua by Diego Martínez <lkaezadl3@gmail.com>
local BI_RGB = 0
local OS2INFOHEADERSIZE = 12
local WININFOHEADERSIZE = 40
local figetw = ioh.read_int16_le
local figetl = ioh.read_int32_le
local function fgetc(f) return (f:read(1) or " "):byte() end
--[[ read_bmfileheader:
| Reads a BMP file header and check that it has the BMP magic number.
]]
local function read_bmfileheader(f, fileheader)
fileheader.bfType = figetw(f)
fileheader.bfSize = figetl(f)
fileheader.bfReserved1 = figetw(f)
fileheader.bfReserved2 = figetw(f)
fileheader.bfOffBits = figetl(f)
if fileheader.bfType ~= 19778 then return false end
return true
end
--[[ read_win_bminfoheader:
| Reads information from a BMP file header.
]]
local function read_win_bminfoheader(f, infoheader)
local win_infoheader
local win_infoheader = { }
win_infoheader.biWidth = figetl(f)
win_infoheader.biHeight = figetl(f)
win_infoheader.biPlanes = figetw(f)
win_infoheader.biBitCount = figetw(f)
win_infoheader.biCompression = figetl(f)
win_infoheader.biSizeImage = figetl(f)
win_infoheader.biXPelsPerMeter = figetl(f)
win_infoheader.biYPelsPerMeter = figetl(f)
win_infoheader.biClrUsed = figetl(f)
win_infoheader.biClrImportant = figetl(f)
infoheader.biWidth = win_infoheader.biWidth
infoheader.biHeight = win_infoheader.biHeight
infoheader.biBitCount = win_infoheader.biBitCount
infoheader.biCompression = win_infoheader.biCompression
return true
end
--[[ read_os2_bminfoheader:
| Reads information from an OS/2 format BMP file header.
]]
local function read_os2_bminfoheader(f, infoheader)
local os2_infoheader = { }
os2_infoheader.biWidth = figetw(f)
os2_infoheader.biHeight = figetw(f)
os2_infoheader.biPlanes = figetw(f)
os2_infoheader.biBitCount = figetw(f)
infoheader.biWidth = os2_infoheader.biWidth
infoheader.biHeight = os2_infoheader.biHeight
infoheader.biBitCount = os2_infoheader.biBitCount
infoheader.biCompression = 0
return true
end
--[[ read_24bit_line:
| Support function for reading the 24 bit bitmap file format, doing
| our best to convert it down to a 256 color palette.
]]
local function read_24bit_line(length, f, line)
local ii = 0
for i = 1, length do
local c = { }
c.b = fgetc(f)
c.g = fgetc(f)
c.r = fgetc(f)
c.a = 255
line[i] = c
ii = ii + 1
end
-- padding
ii = (ii * 3) % 4
if ii ~= 0 then
while ii < 4 do
fgetc(f)
ii = ii + 1
end
end
end
--[[ read_32bit_line:
| Support function for reading the 32 bit bitmap file format, doing
| our best to convert it down to a 256 color palette.
]]
local function read_32bit_line(length, f, line)
for i = 1, length do
local c = { }
c.b = fgetc(f)
c.g = fgetc(f)
c.r = fgetc(f)
c.a = fgetc(f)
line[i] = c
end
end
--[[ read_image:
| For reading the noncompressed BMP image format.
]]
local function read_image(f, bmp, infoheader)
local i, line, height, dir
height = infoheader.biHeight
line = (height < 0) and 1 or height
dir = (height < 0) and 1 or -1
height = math.abs(height)
print(("[imageloader.bmp] size=%dx%d bpp=%d"):format(
infoheader.biWidth,
infoheader.biHeight,
infoheader.biBitCount
))
bmp.pixels = { }
for i = 1, height do
local row = { }
bmp.pixels[line] = row
if infoheader.biBitCount == 24 then
read_24bit_line(infoheader.biWidth, f, row)
elseif infoheader.biBitCount == 32 then
read_32bit_line(infoheader.biWidth, f, row)
else
return false
end
line = line + dir
end
return true
end
local function get_bmp_infoheader(f)
local fileheader = { }
local infoheader = { }
local bmp, biSize
local bpp, dest_depth
if not read_bmfileheader(f, fileheader) then
return nil, "loader_bmp: failed to read file header"
end
biSize = figetl(f)
if biSize == WININFOHEADERSIZE then
if not read_win_bminfoheader(f, infoheader) then
return nil, "loader_bmp: failed to read info header"
end
elseif biSize == OS2INFOHEADERSIZE then
if not read_os2_bminfoheader(f, infoheader) then
return nil, "loader_bmp: failed to read info header"
end
else
return nil, "loader_bmp: unsupported file format"
end
if --[[(infoheader.biBitCount == 8) or (infoheader.biBitCount == 16)
or --]](infoheader.biBitCount == 24) or (infoheader.biBitCount == 32) then
bpp = infoheader.biBitCount
else
return nil, "loader_bmp: unsupported color depth "..infoheader.biBitCount
end
if infoheader.biCompression ~= BI_RGB then
return nil, "loader_bmp: unsupported compression scheme: "..infoheader.biCompression
end
return infoheader
end
--[[ load_bmp_pf:
| Like load_bmp, but starts loading from the current place in the PACKFILE
| specified. If successful the offset into the file will be left just after
| the image data. If unsuccessful the offset into the file is unspecified,
| i.e. you must either reset the offset to some known place or close the
| packfile. The packfile is not closed by this function.
]]
local function load_bmp_pf(f)
local infoheader, e = get_bmp_infoheader(f)
if not infoheader then return nil, e end
local bmp = {
bpp = bpp,
w = infoheader.biWidth,
h = math.abs(infoheader.biHeight),
}
read_image(f, bmp, infoheader)
return bmp
end
local function check_bmp(filename)
local f, e = io.open(filename, "rb")
if not f then return nil, e end
local r, e = get_bmp_infoheader(f)
if e then print(e) end
f:close()
return r, e
end
local function load_bmp(filename)
local f, e = io.open(filename, "rb")
if not f then return nil, e end
local r, e = load_bmp_pf(f)
f:close()
return r, e
end
imageloader.register_type({
description = "Windows or OS/2 Bitmap",
load = load_bmp,
check = check_bmp,
})