The BMP file format, also known as bitmap image file or device independent bitmap (DIB) file format or simply a bitmap, is a raster graphics image file format used to store bitmap digital images, independently of the display device (such as a graphics adapter), especially on Microsoft Windows and OS/2 operating systems.
Great collection of various BMP sample files: BMP Suite Image List (by Jason Summers)
If only there was such a comprehensive sample suite for every file format! It's like a dream for every developer of any binary file format parser. It contains a lot of different types and variations of BMP files, even the tricky ones, where it's not clear from the specification how to deal with them (marked there as "questionable").
If you make a program which will be able to read all the "good" and "questionable" BMP files and won't crash on the "bad" ones, it will definitely have one of the most extensive support of BMP files in the universe!
A beneficial discussion on Adobe forum (archived): Invalid BMP Format with Alpha channel
In 2010, someone noticed that Photoshop generated BMP with an odd type of header. There wasn't any documentation available for this header at the time (and still isn't). However, Chris Cox (former Adobe employee) claimed that they hadn't invented any type of proprietary header and everything they were writing was taken directly from the Microsoft documentation.
It showed up that the unknown header was called BITMAPV3INFOHEADER. Although Microsoft has apparently requested and verified the use of the header, the documentation on MSDN has probably got lost and they have probably forgotten about this type of header.
This is the only source I could find about these structures, so we could't rely on it so much, but I think supporting them as a read-only format won't harm anything. Due to the fact that it isn't documented anywhere else, most applications don't support it.
All Windows headers at once (including mentioned BITMAPV2INFOHEADER and BITMAPV3INFOHEADER):
This page hosts a formal specification of .bmp file format using Kaitai Struct. This specification can be automatically translated into a variety of programming languages to get a parsing library.
import kaitai_struct_nim_runtime
import options
type
Bmp* = ref object of KaitaiStruct
`fileHdr`*: Bmp_FileHeader
`dibInfo`*: Bmp_BitmapInfo
`bitmap`*: Bmp_Bitmap
`parent`*: KaitaiStruct
`rawDibInfo`*: seq[byte]
`rawBitmap`*: seq[byte]
Bmp_Intent* = enum
business = 1
graphics = 2
images = 4
abs_colorimetric = 8
Bmp_ColorSpace* = enum
calibrated_rgb = 0
profile_linked = 1279872587
profile_embedded = 1296188740
windows = 1466527264
s_rgb = 1934772034
Bmp_Os2Rendering* = enum
no_halftoning = 0
error_diffusion = 1
panda = 2
super_circle = 3
Bmp_HeaderType* = enum
bitmap_core_header = 12
bitmap_info_header = 40
bitmap_v2_info_header = 52
bitmap_v3_info_header = 56
os2_2x_bitmap_header = 64
bitmap_v4_header = 108
bitmap_v5_header = 124
Bmp_Compressions* = enum
rgb = 0
rle8 = 1
rle4 = 2
bitfields = 3
jpeg = 4
png = 5
alpha_bitfields = 6
Bmp_Os2Compressions* = enum
rgb = 0
rle8 = 1
rle4 = 2
huffman_1d = 3
rle24 = 4
Bmp_CieXyz* = ref object of KaitaiStruct
`x`*: Bmp_FixedPoint2Dot30
`y`*: Bmp_FixedPoint2Dot30
`z`*: Bmp_FixedPoint2Dot30
`parent`*: Bmp_BitmapV4Extension
Bmp_RgbRecord* = ref object of KaitaiStruct
`blue`*: uint8
`green`*: uint8
`red`*: uint8
`reserved`*: uint8
`hasReservedField`*: bool
`parent`*: Bmp_ColorTable
Bmp_BitmapV5Extension* = ref object of KaitaiStruct
`intent`*: Bmp_Intent
`ofsProfile`*: uint32
`lenProfile`*: uint32
`reserved`*: uint32
`parent`*: Bmp_BitmapHeader
`hasProfileInst`: bool
`hasProfileInstFlag`: bool
`profileDataInst`: KaitaiStruct
`profileDataInstFlag`: bool
Bmp_ColorMask* = ref object of KaitaiStruct
`redMask`*: uint32
`greenMask`*: uint32
`blueMask`*: uint32
`alphaMask`*: uint32
`hasAlphaMask`*: bool
`parent`*: KaitaiStruct
Bmp_BitmapV4Extension* = ref object of KaitaiStruct
`colorSpaceType`*: Bmp_ColorSpace
`endpointRed`*: Bmp_CieXyz
`endpointGreen`*: Bmp_CieXyz
`endpointBlue`*: Bmp_CieXyz
`gammaRed`*: Bmp_FixedPoint16Dot16
`gammaBlue`*: Bmp_FixedPoint16Dot16
`gammaGreen`*: Bmp_FixedPoint16Dot16
`parent`*: Bmp_BitmapHeader
Bmp_BitmapInfoExtension* = ref object of KaitaiStruct
`compression`*: Bmp_Compressions
`os2Compression`*: Bmp_Os2Compressions
`lenImage`*: uint32
`xResolution`*: uint32
`yResolution`*: uint32
`numColorsUsed`*: uint32
`numColorsImportant`*: uint32
`parent`*: Bmp_BitmapHeader
Bmp_FixedPoint2Dot30* = ref object of KaitaiStruct
`raw`*: uint32
`parent`*: Bmp_CieXyz
`valueInst`: float64
`valueInstFlag`: bool
Bmp_Bitmap* = ref object of KaitaiStruct
`parent`*: Bmp
Bmp_BitmapHeader* = ref object of KaitaiStruct
`imageWidth`*: uint32
`imageHeightRaw`*: int32
`numPlanes`*: uint16
`bitsPerPixel`*: uint16
`bitmapInfoExt`*: Bmp_BitmapInfoExtension
`colorMask`*: Bmp_ColorMask
`os22xBitmapExt`*: Bmp_Os22xBitmapExtension
`bitmapV4Ext`*: Bmp_BitmapV4Extension
`bitmapV5Ext`*: Bmp_BitmapV5Extension
`lenHeader`*: uint32
`parent`*: Bmp_BitmapInfo
`extendsBitmapV4Inst`: bool
`extendsBitmapV4InstFlag`: bool
`extendsOs22xBitmapInst`: bool
`extendsOs22xBitmapInstFlag`: bool
`usesFixedPaletteInst`: bool
`usesFixedPaletteInstFlag`: bool
`extendsBitmapInfoInst`: bool
`extendsBitmapInfoInstFlag`: bool
`imageHeightInst`: int
`imageHeightInstFlag`: bool
`isCoreHeaderInst`: bool
`isCoreHeaderInstFlag`: bool
`extendsBitmapV5Inst`: bool
`extendsBitmapV5InstFlag`: bool
`isColorMaskHereInst`: bool
`isColorMaskHereInstFlag`: bool
`bottomUpInst`: bool
`bottomUpInstFlag`: bool
Bmp_Os22xBitmapExtension* = ref object of KaitaiStruct
`units`*: uint16
`reserved`*: uint16
`recording`*: uint16
`rendering`*: Bmp_Os2Rendering
`size1`*: uint32
`size2`*: uint32
`colorEncoding`*: uint32
`identifier`*: uint32
`parent`*: Bmp_BitmapHeader
Bmp_FixedPoint16Dot16* = ref object of KaitaiStruct
`raw`*: uint32
`parent`*: Bmp_BitmapV4Extension
`valueInst`: float64
`valueInstFlag`: bool
Bmp_ColorTable* = ref object of KaitaiStruct
`colors`*: seq[Bmp_RgbRecord]
`hasReservedField`*: bool
`numColors`*: uint32
`parent`*: Bmp_BitmapInfo
`numColorsPresentInst`: int
`numColorsPresentInstFlag`: bool
Bmp_FileHeader* = ref object of KaitaiStruct
`fileType`*: seq[byte]
`lenFile`*: uint32
`reserved1`*: uint16
`reserved2`*: uint16
`ofsBitmap`*: int32
`parent`*: Bmp
Bmp_BitmapInfo* = ref object of KaitaiStruct
`lenHeader`*: uint32
`header`*: Bmp_BitmapHeader
`colorMask`*: Bmp_ColorMask
`colorTable`*: Bmp_ColorTable
`parent`*: Bmp
`rawHeader`*: seq[byte]
`rawColorTable`*: seq[byte]
`isColorMaskGivenInst`: bool
`isColorMaskGivenInstFlag`: bool
`colorMaskGivenInst`: Bmp_ColorMask
`colorMaskGivenInstFlag`: bool
`colorMaskBlueInst`: uint32
`colorMaskBlueInstFlag`: bool
`colorMaskAlphaInst`: uint32
`colorMaskAlphaInstFlag`: bool
`colorMaskGreenInst`: int
`colorMaskGreenInstFlag`: bool
`isColorMaskHereInst`: bool
`isColorMaskHereInstFlag`: bool
`colorMaskRedInst`: int
`colorMaskRedInstFlag`: bool
proc read*(_: typedesc[Bmp], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Bmp
proc read*(_: typedesc[Bmp_CieXyz], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapV4Extension): Bmp_CieXyz
proc read*(_: typedesc[Bmp_RgbRecord], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_ColorTable, hasReservedField: any): Bmp_RgbRecord
proc read*(_: typedesc[Bmp_BitmapV5Extension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapV5Extension
proc read*(_: typedesc[Bmp_ColorMask], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct, hasAlphaMask: any): Bmp_ColorMask
proc read*(_: typedesc[Bmp_BitmapV4Extension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapV4Extension
proc read*(_: typedesc[Bmp_BitmapInfoExtension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapInfoExtension
proc read*(_: typedesc[Bmp_FixedPoint2Dot30], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_CieXyz): Bmp_FixedPoint2Dot30
proc read*(_: typedesc[Bmp_Bitmap], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_Bitmap
proc read*(_: typedesc[Bmp_BitmapHeader], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapInfo, lenHeader: any): Bmp_BitmapHeader
proc read*(_: typedesc[Bmp_Os22xBitmapExtension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_Os22xBitmapExtension
proc read*(_: typedesc[Bmp_FixedPoint16Dot16], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapV4Extension): Bmp_FixedPoint16Dot16
proc read*(_: typedesc[Bmp_ColorTable], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapInfo, hasReservedField: any, numColors: any): Bmp_ColorTable
proc read*(_: typedesc[Bmp_FileHeader], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_FileHeader
proc read*(_: typedesc[Bmp_BitmapInfo], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_BitmapInfo
proc hasProfile*(this: Bmp_BitmapV5Extension): bool
proc profileData*(this: Bmp_BitmapV5Extension): KaitaiStruct
proc value*(this: Bmp_FixedPoint2Dot30): float64
proc extendsBitmapV4*(this: Bmp_BitmapHeader): bool
proc extendsOs22xBitmap*(this: Bmp_BitmapHeader): bool
proc usesFixedPalette*(this: Bmp_BitmapHeader): bool
proc extendsBitmapInfo*(this: Bmp_BitmapHeader): bool
proc imageHeight*(this: Bmp_BitmapHeader): int
proc isCoreHeader*(this: Bmp_BitmapHeader): bool
proc extendsBitmapV5*(this: Bmp_BitmapHeader): bool
proc isColorMaskHere*(this: Bmp_BitmapHeader): bool
proc bottomUp*(this: Bmp_BitmapHeader): bool
proc value*(this: Bmp_FixedPoint16Dot16): float64
proc numColorsPresent*(this: Bmp_ColorTable): int
proc isColorMaskGiven*(this: Bmp_BitmapInfo): bool
proc colorMaskGiven*(this: Bmp_BitmapInfo): Bmp_ColorMask
proc colorMaskBlue*(this: Bmp_BitmapInfo): uint32
proc colorMaskAlpha*(this: Bmp_BitmapInfo): uint32
proc colorMaskGreen*(this: Bmp_BitmapInfo): int
proc isColorMaskHere*(this: Bmp_BitmapInfo): bool
proc colorMaskRed*(this: Bmp_BitmapInfo): int
##[
The **BMP file format**, also known as **bitmap image file** or **device independent
bitmap (DIB) file format** or simply a **bitmap**, is a raster graphics image file
format used to store bitmap digital images, independently of the display
device (such as a graphics adapter), especially on Microsoft Windows
and OS/2 operating systems.
## Samples
Great collection of various BMP sample files:
[**BMP Suite Image List**](
http://entropymine.com/jason/bmpsuite/bmpsuite/html/bmpsuite.html
) (by Jason Summers)
If only there was such a comprehensive sample suite for every file format! It's like
a dream for every developer of any binary file format parser. It contains a lot of
different types and variations of BMP files, even the tricky ones, where it's not clear
from the specification how to deal with them (marked there as "**q**uestionable").
If you make a program which will be able to read all the "**g**ood" and "**q**uestionable"
BMP files and won't crash on the "**b**ad" ones, it will definitely have one of the most
extensive support of BMP files in the universe!
## BITMAPV2INFOHEADER and BITMAPV3INFOHEADER
A beneficial discussion on Adobe forum (archived):
[**Invalid BMP Format with Alpha channel**](
https://web.archive.org/web/20150127132443/https://forums.adobe.com/message/3272950
)
In 2010, someone noticed that Photoshop generated BMP with an odd type of header. There wasn't
any documentation available for this header at the time (and still isn't).
However, Chris Cox (former Adobe employee) claimed that they hadn't invented any type
of proprietary header and everything they were writing was taken directly
from the Microsoft documentation.
It showed up that the unknown header was called BITMAPV3INFOHEADER.
Although Microsoft has apparently requested and verified the use of the header,
the documentation on MSDN has probably got lost and they have probably
forgotten about this type of header.
This is the only source I could find about these structures, so we could't rely
on it so much, but I think supporting them as a read-only format won't harm anything.
Due to the fact that it isn't documented anywhere else, most applications don't support it.
All Windows headers at once (including mentioned BITMAPV2INFOHEADER and BITMAPV3INFOHEADER):
![Bitmap headers overview](
https://web.archive.org/web/20190527043845/https://forums.adobe.com/servlet/JiveServlet/showImage/2-3273299-47801/BMP_Headers.png
)
## Specs
* [Bitmap Storage (Windows Dev Center)](
https://learn.microsoft.com/en-us/windows/win32/gdi/bitmap-storage
)
* BITMAPFILEHEADER
* BITMAPINFOHEADER
* BITMAPV4HEADER
* BITMAPV5HEADER
* [OS/2 Bitmap File Format](
https://www.fileformat.info/format/os2bmp/egff.htm
)
* BITMAPFILEHEADER (OS2BMPFILEHEADER)
* BITMAPCOREHEADER (OS21XBITMAPHEADER)
* OS22XBITMAPHEADER
* [Microsoft Windows Bitmap](
http://netghost.narod.ru/gff/graphics/summary/micbmp.htm
)
* BITMAPFILEHEADER (WINBMPFILEHEADER)
* BITMAPCOREHEADER (WIN2XBITMAPHEADER)
* BITMAPINFOHEADER (WINNTBITMAPHEADER)
* BITMAPV4HEADER (WIN4XBITMAPHEADER)
]##
proc read*(_: typedesc[Bmp], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct): Bmp =
template this: untyped = result
this = new(Bmp)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let fileHdrExpr = Bmp_FileHeader.read(this.io, this.root, this)
this.fileHdr = fileHdrExpr
let rawDibInfoExpr = this.io.readBytes(int((this.fileHdr.ofsBitmap - 14)))
this.rawDibInfo = rawDibInfoExpr
let rawDibInfoIo = newKaitaiStream(rawDibInfoExpr)
let dibInfoExpr = Bmp_BitmapInfo.read(rawDibInfoIo, this.root, this)
this.dibInfo = dibInfoExpr
let rawBitmapExpr = this.io.readBytesFull()
this.rawBitmap = rawBitmapExpr
let rawBitmapIo = newKaitaiStream(rawBitmapExpr)
let bitmapExpr = Bmp_Bitmap.read(rawBitmapIo, this.root, this)
this.bitmap = bitmapExpr
proc fromFile*(_: typedesc[Bmp], filename: string): Bmp =
Bmp.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-ciexyz">Source</a>
]##
proc read*(_: typedesc[Bmp_CieXyz], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapV4Extension): Bmp_CieXyz =
template this: untyped = result
this = new(Bmp_CieXyz)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let xExpr = Bmp_FixedPoint2Dot30.read(this.io, this.root, this)
this.x = xExpr
let yExpr = Bmp_FixedPoint2Dot30.read(this.io, this.root, this)
this.y = yExpr
let zExpr = Bmp_FixedPoint2Dot30.read(this.io, this.root, this)
this.z = zExpr
proc fromFile*(_: typedesc[Bmp_CieXyz], filename: string): Bmp_CieXyz =
Bmp_CieXyz.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Bmp_RgbRecord], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_ColorTable, hasReservedField: any): Bmp_RgbRecord =
template this: untyped = result
this = new(Bmp_RgbRecord)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let hasReservedFieldExpr = bool(hasReservedField)
this.hasReservedField = hasReservedFieldExpr
let blueExpr = this.io.readU1()
this.blue = blueExpr
let greenExpr = this.io.readU1()
this.green = greenExpr
let redExpr = this.io.readU1()
this.red = redExpr
if this.hasReservedField:
let reservedExpr = this.io.readU1()
this.reserved = reservedExpr
proc fromFile*(_: typedesc[Bmp_RgbRecord], filename: string): Bmp_RgbRecord =
Bmp_RgbRecord.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv5header">Source</a>
]##
proc read*(_: typedesc[Bmp_BitmapV5Extension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapV5Extension =
template this: untyped = result
this = new(Bmp_BitmapV5Extension)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let intentExpr = Bmp_Intent(this.io.readU4le())
this.intent = intentExpr
##[
The offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
]##
let ofsProfileExpr = this.io.readU4le()
this.ofsProfile = ofsProfileExpr
let lenProfileExpr = this.io.readU4le()
this.lenProfile = lenProfileExpr
let reservedExpr = this.io.readU4le()
this.reserved = reservedExpr
proc hasProfile(this: Bmp_BitmapV5Extension): bool =
if this.hasProfileInstFlag:
return this.hasProfileInst
let hasProfileInstExpr = bool( ((this.parent.bitmapV4Ext.colorSpaceType == bmp.profile_linked) or (this.parent.bitmapV4Ext.colorSpaceType == bmp.profile_embedded)) )
this.hasProfileInst = hasProfileInstExpr
this.hasProfileInstFlag = true
return this.hasProfileInst
proc profileData(this: Bmp_BitmapV5Extension): KaitaiStruct =
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/wcs/using-structures-in-wcs-1-0">"If the profile is embedded, profile data is the actual profile, and if it is linked, the profile data is the null-terminated file name of the profile. This cannot be a Unicode string. It must be composed exclusively of characters from the Windows character set (code page 1252)."</a>
]##
if this.profileDataInstFlag:
return this.profileDataInst
if this.hasProfile:
let io = Bmp(this.root).io
let pos = io.pos()
io.seek(int((14 + this.ofsProfile)))
block:
let on = this.parent.bitmapV4Ext.colorSpaceType == bmp.profile_linked
if on == true:
let profileDataInstExpr = encode(io.readBytes(int(this.lenProfile)).bytesTerminate(0, false), "windows-1252")
this.profileDataInst = profileDataInstExpr
else:
let profileDataInstExpr = io.readBytes(int(this.lenProfile))
this.profileDataInst = profileDataInstExpr
io.seek(pos)
this.profileDataInstFlag = true
return this.profileDataInst
proc fromFile*(_: typedesc[Bmp_BitmapV5Extension], filename: string): Bmp_BitmapV5Extension =
Bmp_BitmapV5Extension.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Bmp_ColorMask], io: KaitaiStream, root: KaitaiStruct, parent: KaitaiStruct, hasAlphaMask: any): Bmp_ColorMask =
template this: untyped = result
this = new(Bmp_ColorMask)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let hasAlphaMaskExpr = bool(hasAlphaMask)
this.hasAlphaMask = hasAlphaMaskExpr
let redMaskExpr = this.io.readU4le()
this.redMask = redMaskExpr
let greenMaskExpr = this.io.readU4le()
this.greenMask = greenMaskExpr
let blueMaskExpr = this.io.readU4le()
this.blueMask = blueMaskExpr
if this.hasAlphaMask:
let alphaMaskExpr = this.io.readU4le()
this.alphaMask = alphaMaskExpr
proc fromFile*(_: typedesc[Bmp_ColorMask], filename: string): Bmp_ColorMask =
Bmp_ColorMask.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapv4header">Source</a>
]##
proc read*(_: typedesc[Bmp_BitmapV4Extension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapV4Extension =
template this: untyped = result
this = new(Bmp_BitmapV4Extension)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let colorSpaceTypeExpr = Bmp_ColorSpace(this.io.readU4le())
this.colorSpaceType = colorSpaceTypeExpr
let endpointRedExpr = Bmp_CieXyz.read(this.io, this.root, this)
this.endpointRed = endpointRedExpr
let endpointGreenExpr = Bmp_CieXyz.read(this.io, this.root, this)
this.endpointGreen = endpointGreenExpr
let endpointBlueExpr = Bmp_CieXyz.read(this.io, this.root, this)
this.endpointBlue = endpointBlueExpr
let gammaRedExpr = Bmp_FixedPoint16Dot16.read(this.io, this.root, this)
this.gammaRed = gammaRedExpr
let gammaBlueExpr = Bmp_FixedPoint16Dot16.read(this.io, this.root, this)
this.gammaBlue = gammaBlueExpr
let gammaGreenExpr = Bmp_FixedPoint16Dot16.read(this.io, this.root, this)
this.gammaGreen = gammaGreenExpr
proc fromFile*(_: typedesc[Bmp_BitmapV4Extension], filename: string): Bmp_BitmapV4Extension =
Bmp_BitmapV4Extension.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/previous-versions/dd183376(v=vs.85)">Source</a>
]##
proc read*(_: typedesc[Bmp_BitmapInfoExtension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_BitmapInfoExtension =
template this: untyped = result
this = new(Bmp_BitmapInfoExtension)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
if not(this.parent.extendsOs22xBitmap):
let compressionExpr = Bmp_Compressions(this.io.readU4le())
this.compression = compressionExpr
if this.parent.extendsOs22xBitmap:
let os2CompressionExpr = Bmp_Os2Compressions(this.io.readU4le())
this.os2Compression = os2CompressionExpr
##[
If biCompression is BI_JPEG or BI_PNG, indicates the size of the JPEG or PNG image buffer.
This may be set to zero for BI_RGB bitmaps.
]##
let lenImageExpr = this.io.readU4le()
this.lenImage = lenImageExpr
let xResolutionExpr = this.io.readU4le()
this.xResolution = xResolutionExpr
let yResolutionExpr = this.io.readU4le()
this.yResolution = yResolutionExpr
let numColorsUsedExpr = this.io.readU4le()
this.numColorsUsed = numColorsUsedExpr
let numColorsImportantExpr = this.io.readU4le()
this.numColorsImportant = numColorsImportantExpr
proc fromFile*(_: typedesc[Bmp_BitmapInfoExtension], filename: string): Bmp_BitmapInfoExtension =
Bmp_BitmapInfoExtension.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Bmp_FixedPoint2Dot30], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_CieXyz): Bmp_FixedPoint2Dot30 =
template this: untyped = result
this = new(Bmp_FixedPoint2Dot30)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let rawExpr = this.io.readU4le()
this.raw = rawExpr
proc value(this: Bmp_FixedPoint2Dot30): float64 =
if this.valueInstFlag:
return this.valueInst
let valueInstExpr = float64(((this.raw + 0.0) div (1 shl 30)))
this.valueInst = valueInstExpr
this.valueInstFlag = true
return this.valueInst
proc fromFile*(_: typedesc[Bmp_FixedPoint2Dot30], filename: string): Bmp_FixedPoint2Dot30 =
Bmp_FixedPoint2Dot30.read(newKaitaiFileStream(filename), nil, nil)
##[
Replace with an opaque type if you care about the pixels. You can look at
an example of a JavaScript implementation:
<https://github.com/generalmimon/bmptool/blob/master/src/Bitmap.js>
There is a proposal for adding bitmap data type to Kaitai Struct:
<https://github.com/kaitai-io/kaitai_struct/issues/188>
]##
proc read*(_: typedesc[Bmp_Bitmap], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_Bitmap =
template this: untyped = result
this = new(Bmp_Bitmap)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
proc fromFile*(_: typedesc[Bmp_Bitmap], filename: string): Bmp_Bitmap =
Bmp_Bitmap.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapcoreheader">Source</a>
@see <a href="https://www.fileformat.info/format/os2bmp/egff.htm#OS2BMP-DMYID.3.1">Source</a>
]##
proc read*(_: typedesc[Bmp_BitmapHeader], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapInfo, lenHeader: any): Bmp_BitmapHeader =
template this: untyped = result
this = new(Bmp_BitmapHeader)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let lenHeaderExpr = uint32(lenHeader)
this.lenHeader = lenHeaderExpr
##[
Image width, px
]##
block:
let on = this.isCoreHeader
if on == true:
let imageWidthExpr = uint32(this.io.readU2le())
this.imageWidth = imageWidthExpr
elif on == false:
let imageWidthExpr = this.io.readU4le()
this.imageWidth = imageWidthExpr
##[
Image height, px (positive => bottom-up image, negative => top-down image)
]##
block:
let on = this.isCoreHeader
if on == true:
let imageHeightRawExpr = int32(this.io.readS2le())
this.imageHeightRaw = imageHeightRawExpr
elif on == false:
let imageHeightRawExpr = this.io.readS4le()
this.imageHeightRaw = imageHeightRawExpr
##[
Number of planes for target device, must be 1
]##
let numPlanesExpr = this.io.readU2le()
this.numPlanes = numPlanesExpr
##[
Number of bits per pixel that image buffer uses (1, 4, 8, 16, 24 or 32)
]##
let bitsPerPixelExpr = this.io.readU2le()
this.bitsPerPixel = bitsPerPixelExpr
if this.extendsBitmapInfo:
let bitmapInfoExtExpr = Bmp_BitmapInfoExtension.read(this.io, this.root, this)
this.bitmapInfoExt = bitmapInfoExtExpr
if this.isColorMaskHere:
let colorMaskExpr = Bmp_ColorMask.read(this.io, this.root, this, this.lenHeader != ord(bmp.bitmap_v2_info_header))
this.colorMask = colorMaskExpr
if this.extendsOs22xBitmap:
let os22xBitmapExtExpr = Bmp_Os22xBitmapExtension.read(this.io, this.root, this)
this.os22xBitmapExt = os22xBitmapExtExpr
if this.extendsBitmapV4:
let bitmapV4ExtExpr = Bmp_BitmapV4Extension.read(this.io, this.root, this)
this.bitmapV4Ext = bitmapV4ExtExpr
if this.extendsBitmapV5:
let bitmapV5ExtExpr = Bmp_BitmapV5Extension.read(this.io, this.root, this)
this.bitmapV5Ext = bitmapV5ExtExpr
proc extendsBitmapV4(this: Bmp_BitmapHeader): bool =
if this.extendsBitmapV4InstFlag:
return this.extendsBitmapV4Inst
let extendsBitmapV4InstExpr = bool(this.lenHeader >= ord(bmp.bitmap_v4_header))
this.extendsBitmapV4Inst = extendsBitmapV4InstExpr
this.extendsBitmapV4InstFlag = true
return this.extendsBitmapV4Inst
proc extendsOs22xBitmap(this: Bmp_BitmapHeader): bool =
if this.extendsOs22xBitmapInstFlag:
return this.extendsOs22xBitmapInst
let extendsOs22xBitmapInstExpr = bool(this.lenHeader == ord(bmp.os2_2x_bitmap_header))
this.extendsOs22xBitmapInst = extendsOs22xBitmapInstExpr
this.extendsOs22xBitmapInstFlag = true
return this.extendsOs22xBitmapInst
proc usesFixedPalette(this: Bmp_BitmapHeader): bool =
if this.usesFixedPaletteInstFlag:
return this.usesFixedPaletteInst
let usesFixedPaletteInstExpr = bool( ((not( ((this.bitsPerPixel == 16) or (this.bitsPerPixel == 24) or (this.bitsPerPixel == 32)) )) and (not( ((this.extendsBitmapInfo) and (not(this.extendsOs22xBitmap)) and ( ((this.bitmapInfoExt.compression == bmp.jpeg) or (this.bitmapInfoExt.compression == bmp.png)) )) ))) )
this.usesFixedPaletteInst = usesFixedPaletteInstExpr
this.usesFixedPaletteInstFlag = true
return this.usesFixedPaletteInst
proc extendsBitmapInfo(this: Bmp_BitmapHeader): bool =
if this.extendsBitmapInfoInstFlag:
return this.extendsBitmapInfoInst
let extendsBitmapInfoInstExpr = bool(this.lenHeader >= ord(bmp.bitmap_info_header))
this.extendsBitmapInfoInst = extendsBitmapInfoInstExpr
this.extendsBitmapInfoInstFlag = true
return this.extendsBitmapInfoInst
proc imageHeight(this: Bmp_BitmapHeader): int =
if this.imageHeightInstFlag:
return this.imageHeightInst
let imageHeightInstExpr = int((if this.imageHeightRaw < 0: -(this.imageHeightRaw) else: this.imageHeightRaw))
this.imageHeightInst = imageHeightInstExpr
this.imageHeightInstFlag = true
return this.imageHeightInst
proc isCoreHeader(this: Bmp_BitmapHeader): bool =
if this.isCoreHeaderInstFlag:
return this.isCoreHeaderInst
let isCoreHeaderInstExpr = bool(this.lenHeader == ord(bmp.bitmap_core_header))
this.isCoreHeaderInst = isCoreHeaderInstExpr
this.isCoreHeaderInstFlag = true
return this.isCoreHeaderInst
proc extendsBitmapV5(this: Bmp_BitmapHeader): bool =
if this.extendsBitmapV5InstFlag:
return this.extendsBitmapV5Inst
let extendsBitmapV5InstExpr = bool(this.lenHeader >= ord(bmp.bitmap_v5_header))
this.extendsBitmapV5Inst = extendsBitmapV5InstExpr
this.extendsBitmapV5InstFlag = true
return this.extendsBitmapV5Inst
proc isColorMaskHere(this: Bmp_BitmapHeader): bool =
if this.isColorMaskHereInstFlag:
return this.isColorMaskHereInst
let isColorMaskHereInstExpr = bool( ((this.lenHeader == ord(bmp.bitmap_v2_info_header)) or (this.lenHeader == ord(bmp.bitmap_v3_info_header)) or (this.extendsBitmapV4)) )
this.isColorMaskHereInst = isColorMaskHereInstExpr
this.isColorMaskHereInstFlag = true
return this.isColorMaskHereInst
proc bottomUp(this: Bmp_BitmapHeader): bool =
if this.bottomUpInstFlag:
return this.bottomUpInst
let bottomUpInstExpr = bool(this.imageHeightRaw > 0)
this.bottomUpInst = bottomUpInstExpr
this.bottomUpInstFlag = true
return this.bottomUpInst
proc fromFile*(_: typedesc[Bmp_BitmapHeader], filename: string): Bmp_BitmapHeader =
Bmp_BitmapHeader.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://www.fileformat.info/format/os2bmp/egff.htm#OS2BMP-DMYID.3.2">Source</a>
]##
proc read*(_: typedesc[Bmp_Os22xBitmapExtension], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapHeader): Bmp_Os22xBitmapExtension =
template this: untyped = result
this = new(Bmp_Os22xBitmapExtension)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let unitsExpr = this.io.readU2le()
this.units = unitsExpr
let reservedExpr = this.io.readU2le()
this.reserved = reservedExpr
##[
Specifies how the bitmap scan lines are stored.
The only valid value for this field is 0, indicating that the bitmap is
stored from left to right and from the bottom up.
]##
let recordingExpr = this.io.readU2le()
this.recording = recordingExpr
##[
Specifies the halftoning algorithm used on the bitmap data.
]##
let renderingExpr = Bmp_Os2Rendering(this.io.readU2le())
this.rendering = renderingExpr
##[
rendering == os2_rendering::error_diffusion
=> error damping as a percentage in the range 0 through 100
rendering == os2_rendering::panda or rendering == os2_rendering::super_circle
=> X dimension of the pattern used in pixels
]##
let size1Expr = this.io.readU4le()
this.size1 = size1Expr
##[
rendering == os2_rendering::error_diffusion
=> not used
rendering == os2_rendering::panda or rendering == os2_rendering::super_circle
=> Y dimension of the pattern used in pixels
]##
let size2Expr = this.io.readU4le()
this.size2 = size2Expr
##[
Specifies the color model used to describe the bitmap data.
The only valid value is 0, indicating the RGB encoding scheme.
]##
let colorEncodingExpr = this.io.readU4le()
this.colorEncoding = colorEncodingExpr
##[
Application-specific value
]##
let identifierExpr = this.io.readU4le()
this.identifier = identifierExpr
proc fromFile*(_: typedesc[Bmp_Os22xBitmapExtension], filename: string): Bmp_Os22xBitmapExtension =
Bmp_Os22xBitmapExtension.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Bmp_FixedPoint16Dot16], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapV4Extension): Bmp_FixedPoint16Dot16 =
template this: untyped = result
this = new(Bmp_FixedPoint16Dot16)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let rawExpr = this.io.readU4le()
this.raw = rawExpr
proc value(this: Bmp_FixedPoint16Dot16): float64 =
if this.valueInstFlag:
return this.valueInst
let valueInstExpr = float64(((this.raw + 0.0) div (1 shl 16)))
this.valueInst = valueInstExpr
this.valueInstFlag = true
return this.valueInst
proc fromFile*(_: typedesc[Bmp_FixedPoint16Dot16], filename: string): Bmp_FixedPoint16Dot16 =
Bmp_FixedPoint16Dot16.read(newKaitaiFileStream(filename), nil, nil)
proc read*(_: typedesc[Bmp_ColorTable], io: KaitaiStream, root: KaitaiStruct, parent: Bmp_BitmapInfo, hasReservedField: any, numColors: any): Bmp_ColorTable =
template this: untyped = result
this = new(Bmp_ColorTable)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let hasReservedFieldExpr = bool(hasReservedField)
this.hasReservedField = hasReservedFieldExpr
let numColorsExpr = uint32(numColors)
this.numColors = numColorsExpr
for i in 0 ..< int((if ((this.numColors > 0) and (this.numColors < this.numColorsPresent)) : this.numColors else: this.numColorsPresent)):
let it = Bmp_RgbRecord.read(this.io, this.root, this, this.hasReservedField)
this.colors.add(it)
proc numColorsPresent(this: Bmp_ColorTable): int =
if this.numColorsPresentInstFlag:
return this.numColorsPresentInst
let numColorsPresentInstExpr = int((this.io.size div (if this.hasReservedField: 4 else: 3)))
this.numColorsPresentInst = numColorsPresentInstExpr
this.numColorsPresentInstFlag = true
return this.numColorsPresentInst
proc fromFile*(_: typedesc[Bmp_ColorTable], filename: string): Bmp_ColorTable =
Bmp_ColorTable.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader">Source</a>
]##
proc read*(_: typedesc[Bmp_FileHeader], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_FileHeader =
template this: untyped = result
this = new(Bmp_FileHeader)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let fileTypeExpr = this.io.readBytes(int(2))
this.fileType = fileTypeExpr
##[
not reliable, mostly ignored by BMP decoders
]##
let lenFileExpr = this.io.readU4le()
this.lenFile = lenFileExpr
let reserved1Expr = this.io.readU2le()
this.reserved1 = reserved1Expr
let reserved2Expr = this.io.readU2le()
this.reserved2 = reserved2Expr
##[
Offset to actual raw pixel data of the image
]##
let ofsBitmapExpr = this.io.readS4le()
this.ofsBitmap = ofsBitmapExpr
proc fromFile*(_: typedesc[Bmp_FileHeader], filename: string): Bmp_FileHeader =
Bmp_FileHeader.read(newKaitaiFileStream(filename), nil, nil)
##[
@see <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo">Source</a>
]##
proc read*(_: typedesc[Bmp_BitmapInfo], io: KaitaiStream, root: KaitaiStruct, parent: Bmp): Bmp_BitmapInfo =
template this: untyped = result
this = new(Bmp_BitmapInfo)
let root = if root == nil: cast[Bmp](this) else: cast[Bmp](root)
this.io = io
this.root = root
this.parent = parent
let lenHeaderExpr = this.io.readU4le()
this.lenHeader = lenHeaderExpr
let rawHeaderExpr = this.io.readBytes(int((this.lenHeader - 4)))
this.rawHeader = rawHeaderExpr
let rawHeaderIo = newKaitaiStream(rawHeaderExpr)
let headerExpr = Bmp_BitmapHeader.read(rawHeaderIo, this.root, this, this.lenHeader)
this.header = headerExpr
##[
Valid only for BITMAPINFOHEADER, in all headers extending it the masks are contained in the header itself.
]##
if this.isColorMaskHere:
let colorMaskExpr = Bmp_ColorMask.read(this.io, this.root, this, this.header.bitmapInfoExt.compression == bmp.alpha_bitfields)
this.colorMask = colorMaskExpr
if not(this.io.isEof):
let rawColorTableExpr = this.io.readBytesFull()
this.rawColorTable = rawColorTableExpr
let rawColorTableIo = newKaitaiStream(rawColorTableExpr)
let colorTableExpr = Bmp_ColorTable.read(rawColorTableIo, this.root, this, not(this.header.isCoreHeader), (if this.header.extendsBitmapInfo: this.header.bitmapInfoExt.numColorsUsed else: 0))
this.colorTable = colorTableExpr
proc isColorMaskGiven(this: Bmp_BitmapInfo): bool =
if this.isColorMaskGivenInstFlag:
return this.isColorMaskGivenInst
let isColorMaskGivenInstExpr = bool( ((this.header.extendsBitmapInfo) and ( ((this.header.bitmapInfoExt.compression == bmp.bitfields) or (this.header.bitmapInfoExt.compression == bmp.alpha_bitfields)) ) and ( ((this.isColorMaskHere) or (this.header.isColorMaskHere)) )) )
this.isColorMaskGivenInst = isColorMaskGivenInstExpr
this.isColorMaskGivenInstFlag = true
return this.isColorMaskGivenInst
proc colorMaskGiven(this: Bmp_BitmapInfo): Bmp_ColorMask =
if this.colorMaskGivenInstFlag:
return this.colorMaskGivenInst
if this.isColorMaskGiven:
let colorMaskGivenInstExpr = Bmp_ColorMask((if this.isColorMaskHere: this.colorMask else: this.header.colorMask))
this.colorMaskGivenInst = colorMaskGivenInstExpr
this.colorMaskGivenInstFlag = true
return this.colorMaskGivenInst
proc colorMaskBlue(this: Bmp_BitmapInfo): uint32 =
if this.colorMaskBlueInstFlag:
return this.colorMaskBlueInst
let colorMaskBlueInstExpr = uint32((if this.isColorMaskGiven: this.colorMaskGiven.blueMask else: (if this.header.bitsPerPixel == 16: 31 else: (if ((this.header.bitsPerPixel == 24) or (this.header.bitsPerPixel == 32)) : 255 else: 0))))
this.colorMaskBlueInst = colorMaskBlueInstExpr
this.colorMaskBlueInstFlag = true
return this.colorMaskBlueInst
proc colorMaskAlpha(this: Bmp_BitmapInfo): uint32 =
if this.colorMaskAlphaInstFlag:
return this.colorMaskAlphaInst
let colorMaskAlphaInstExpr = uint32((if ((this.isColorMaskGiven) and (this.colorMaskGiven.hasAlphaMask)) : this.colorMaskGiven.alphaMask else: 0))
this.colorMaskAlphaInst = colorMaskAlphaInstExpr
this.colorMaskAlphaInstFlag = true
return this.colorMaskAlphaInst
proc colorMaskGreen(this: Bmp_BitmapInfo): int =
if this.colorMaskGreenInstFlag:
return this.colorMaskGreenInst
let colorMaskGreenInstExpr = int((if this.isColorMaskGiven: this.colorMaskGiven.greenMask else: (if this.header.bitsPerPixel == 16: 992 else: (if ((this.header.bitsPerPixel == 24) or (this.header.bitsPerPixel == 32)) : 65280 else: 0))))
this.colorMaskGreenInst = colorMaskGreenInstExpr
this.colorMaskGreenInstFlag = true
return this.colorMaskGreenInst
proc isColorMaskHere(this: Bmp_BitmapInfo): bool =
if this.isColorMaskHereInstFlag:
return this.isColorMaskHereInst
let isColorMaskHereInstExpr = bool( ((not(this.io.isEof)) and (this.header.lenHeader == ord(bmp.bitmap_info_header)) and ( ((this.header.bitmapInfoExt.compression == bmp.bitfields) or (this.header.bitmapInfoExt.compression == bmp.alpha_bitfields)) )) )
this.isColorMaskHereInst = isColorMaskHereInstExpr
this.isColorMaskHereInstFlag = true
return this.isColorMaskHereInst
proc colorMaskRed(this: Bmp_BitmapInfo): int =
if this.colorMaskRedInstFlag:
return this.colorMaskRedInst
let colorMaskRedInstExpr = int((if this.isColorMaskGiven: this.colorMaskGiven.redMask else: (if this.header.bitsPerPixel == 16: 31744 else: (if ((this.header.bitsPerPixel == 24) or (this.header.bitsPerPixel == 32)) : 16711680 else: 0))))
this.colorMaskRedInst = colorMaskRedInstExpr
this.colorMaskRedInstFlag = true
return this.colorMaskRedInst
proc fromFile*(_: typedesc[Bmp_BitmapInfo], filename: string): Bmp_BitmapInfo =
Bmp_BitmapInfo.read(newKaitaiFileStream(filename), nil, nil)