190 lines
5.8 KiB
Python
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)
|
|
|