pycrate/pycrate_media/BMP.py

190 lines
5.8 KiB
Python

# -*- coding: UTF-8 -*-
#/**
# * Software Name : pycrate
# * Version : 0.4
# *
# * Copyright 2016. Benoit Michau. ANSSI.
# *
# * This library is free software; you can redistribute it and/or
# * modify it under the terms of the GNU Lesser General Public
# * License as published by the Free Software Foundation; either
# * version 2.1 of the License, or (at your option) any later version.
# *
# * This library is distributed in the hope that it will be useful,
# * but WITHOUT ANY WARRANTY; without even the implied warranty of
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# * Lesser General Public License for more details.
# *
# * You should have received a copy of the GNU Lesser General Public
# * License along with this library; if not, write to the Free Software
# * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
# * MA 02110-1301 USA
# *
# *--------------------------------------------------------
# * File Name : pycrate_media/BMP.py
# * Created : 2016-03-04
# * Authors : Benoit Michau
# *--------------------------------------------------------
#*/
from pycrate_core.elt import *
from pycrate_core.base import *
from pycrate_core.repr import *
Buf.REPR_MAXLEN = 256
# BMP pixels array, with 2 variants
class PixelRow(Array):
_GEN = UintLE('Pixel', bl=8, rep=REPR_HEX)
#def _from_char(self, char):
# self._log('val before: {0}'.format(self._val))
# Array._from_char(self, char)
# self._log('val after: {0}'.format(self._val))
class PixelRowPad(Envelope):
_GEN = (
PixelRow(),
Buf('padding', rep=REPR_HEX)
)
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[1].set_blauto(self._set_pad_len)
def _set_pad_len(self):
bl = self[0].get_bl()
if bl % 32:
return 32 - (bl%32)
else:
return 0
class PixelArray(Array):
_GEN = PixelRowPad()
class PixelArrayBuf(Array):
_GEN = Buf('PixelRowBuf', rep=REPR_HD)
# BMP headers
class ColorTable(Array):
_GEN = Uint32LE('Color')
class DIBHeader(Envelope):
_GEN = (
Uint32LE('DIBHeaderSize'),
Uint32LE('Width'),
Uint32LE('Height'),
Uint16LE('Planes'),
Uint16LE('BitsPerPixel'),
Uint32LE('Comp'),
Uint32LE('ImageSize'),
Uint32LE('XPixelsPerMeter'),
Uint32LE('YPixelsPerMeter'),
Uint32LE('ColorsInColorTable'),
Uint32LE('ImportantColorCount'),
Uint32LE('RedChannelBitmask', rep=REPR_HEX),
Uint32LE('GreenChannelBitmask', rep=REPR_HEX),
Uint32LE('BlueChannelBitmask', rep=REPR_HEX),
Uint32LE('AlphaChannelBitmask', rep=REPR_HEX),
Uint32LE('ColorSpaceType'),
Uint32LE('ColorSpaceEndpoints'),
Uint32LE('GammaRed'),
Uint32LE('GammaGreen'),
Uint32LE('GammaBlue'),
Uint32LE('Intent'),
Uint32LE('ICCProfileData'),
Uint32LE('ICCProfileSize'),
Uint32LE('Reserved', rep=REPR_HEX),
)
def _from_char(self, char):
# DIB header may be shorter, depending of the BMP encoder used
# non-encoded elements are set transparent
size = char.to_uint_le(32)
if size < 92:
[self[-i].set_trans(True) for i in range(1, 1+((92-size)//4))]
Envelope._from_char(self, char)
class FileHeader(Envelope):
_GEN = (
Buf('Signature', val=b'BM', bl=16),
Uint32LE('Size'),
Buf('Reserved', val=b'\0\0\0\0', bl=32, rep=REPR_HEX),
Uint32LE('Offset')
)
# TODO: check what is implied by multiple "Planes"
# does it mean multiple PixelArray ?
class BMP(Envelope):
_GEN_LBUF = (
FileHeader(),
DIBHeader(hier=1),
ColorTable(hier=2),
Buf('padding', hier=2, rep=REPR_HD),
PixelArrayBuf(hier=2)
)
_GEN_LPIX = (
FileHeader(),
DIBHeader(hier=1),
ColorTable(hier=2),
Buf('padding', hier=2, rep=REPR_HD),
PixelArray(hier=2)
)
_GEN = _GEN_LBUF
def __init__(self, *args, **kwargs):
Envelope.__init__(self, *args, **kwargs)
self[0][1].set_valauto(self._set_fileheader_size)
self[0][3].set_valauto(self._set_fileheader_off)
self[1][0].set_valauto(self._set_dibheader_size)
self[1][9].set_valauto(self._set_dibheader_col)
self[2].set_numauto(self._set_colortable_num)
self[3].set_blauto(self._set_padding_len)
self[4].set_numauto(self._set_pixelrow_num)
def _set_fileheader_size(self):
return 14 + self[1:].get_len()
def _set_fileheader_off(self):
return 14 + self[1:4].get_len()
def _set_dibheader_size(self):
return self[1].get_len()
def _set_dibheader_col(self):
return self[2].get_num()
def _set_colortable_num(self):
return self[1][9].get_val()
def _set_padding_len(self):
return 8 * (self[0][3].get_val() - self[0:3].get_len())
def _set_pixelrow_num(self):
return self[1][2].get_val()
def _from_char(self, char):
self[0:2]._from_char(char)
w = self[1][1].get_val() # width
h = self[1][2].get_val() # height
pbl = self[1][4].get_val() # pixel bitlen
#log('w, h, pbl:', w, h, pbl)
if isinstance(self[4], PixelArrayBuf):
# set PixelRowBuf length in bits (width * bits per pixel)
row_len = w * pbl
if row_len % 32:
row_len += 32 - (row_len % 32)
self[4].set_attrs(num=h, tmpl={'bl': row_len})
else:
# set PixelArray pixel bit length, width and height
self[4].set_attrs(num=h, tmpl={'content':{'PixelRow': {'num':w, 'tmpl':{'bl':pbl}}}})
#log(self[4].get_attrs())
# continue parsing color table and pixel array
self[2:]._from_char(char)