Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Source file bmp.ml
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090(***********************************************************************)(* *)(* Objective Caml *)(* *)(* François Pessaux, projet Cristal, INRIA Rocquencourt *)(* Pierre Weis, projet Cristal, INRIA Rocquencourt *)(* Jun Furuse, projet Cristal, INRIA Rocquencourt *)(* *)(* Copyright 1999-2004 *)(* Institut National de Recherche en Informatique et en Automatique. *)(* Distributed only by permission. *)(* *)(***********************************************************************)(* $Id: bmp.ml,v 1.3 2009/02/08 14:59:17 weis Exp $ *)(* Loading and saving image in the bmp format. *)openImagesopenUtil(*
Caml representation of a bmp bit map image.
Fields are Caml values, decoded versions of raw data in the file.
Structure of bitmaps files on disk :
- BITMAPFILEHEADER : bytes 0 to 14 excluded
- BITMAPINFOHEADER : bytes 14 to 54 excluded
- RGBQUAD [] : color map
- BYTES [] : bit map
*)typebmp={bmpFileHeader:bitmapfileheader;(* Bytes <0 14< *)bmpInfoHeader:bitmapinfoheader;(* Bytes <14 54< *)bmpRgbQuad:rgbarray;(* Bytes <54 ... *)bmpBytes:bytes;(* Bytes <bfOffBits ... *)}andbitmapfileheader={(* WORD: that is 2 bytes *)bfType:int;(* Bytes <0 2< *)(* DWORD: that is 2 WORDs *)bfSize:int;(* Bytes <2 6< *)(* WORD *)bfReserved1:int;(* Bytes <6 8< *)(* WORD *)bfReserved2:int;(* Bytes <8 10< *)(* DWORD *)bfOffBits:int;(* Bytes <10 14< *)}andbitmapinfoheader={(* DWORD *)biSize:int;(* Bytes <14 18< *)(* DWORD *)biWidth:int;(* Bytes <18 22< *)(* DWORD *)biHeight:int;(* Bytes <22 26< *)(* WORD *)biPlanes:int;(* Bytes <26 28< *)(* WORD *)biBitCount:bibitcount;(* Bytes <28 30< *)(* DWORD *)biCompression:bicompression;(* Bytes <30 34< *)(* DWORD *)biSizeImage:int;(* Bytes <34 38< *)(* DWORD *)biXPelsPerMeter:int;(* Bytes <38 42< *)(* DWORD *)biYPelsPerMeter:int;(* Bytes <42 46< *)(* DWORD *)biClrUsed:int;(* Bytes <46 50< *)(* DWORD *)biClrImportant:int(* Bytes <50 54< *)}andbicompression=|BI_RGB(* Specifies that the bitmap is not compressed. *)|BI_RLE8(* Specifies a run-length encoded format for bitmaps with 8 bits
per pixel. The compression format is a two-bytes format
consisting of a count byte followed by a byte containing a color
index. *)|BI_RLE4(* Specifies a run-length encoded format for bitmaps with 4 bits
per pixel. The compression format is a two-byte format consisting of
a count byte followed by two word-length color indexes. *)andbibitcount=|Monochrome(* 1 The bitmap is monochrome, and the bmiColors field must
contain two entries. Each bit in the bitmap array represents a
pixel. If the bit is clear, the pixel is displayed with the
color of the first entry in the bmiColors table; if the bit is
set, the pixel has the color of the second entry in the
table. *)|Color16(* 4 The bitmap has a maximum of 16 colors, and the bmiColors
field contains up to 16 entries. Each pixel in the bitmap is
represented by a four-bit index into the color table.
For example, if the first byte in the bitmap is 0x1F, then the
byte represents two pixels. The first pixel contains the color
in the second table entry, and the second pixel contains the
color in the 16th table entry. *)|Color256(* 8 The bitmap has a maximum of 256 colors, and the bmiColors
field contains up to 256 entries. In this case, each byte in the
array represents a single pixel. *)|ColorRGB(* 24 The bitmap has a maximum of 2^24 colors. The bmiColors
field is NULL, and each three bytes in the bitmap array
represents the relative intensities of red, green, and blue,
respectively, of a pixel. *)|ColorRGBA(* 32 The bitmap, RGBA *)(* =================================================================== *)(* ============= Reading bmp files as Caml images. =================== *)(* =================================================================== *)(* # of bytes read during loading : should be equal to the size of the file *)letbytes_read=ref0letread_byteic=incrbytes_read;input_byteicletskip_byteic=incrbytes_read;ignore(input_byteic)letread_wordic=letb0=read_byteicinletb1=read_byteicin(* little-endian form *)(b1lsl8)+b0letread_dwordic=letb0=read_byteicinletb1=read_byteicinletb2=read_byteicinletb3=read_byteicin(* little-endian form *)(b3lsl24)+(b2lsl16)+(b1lsl8)+b0letread_bit_countic=matchread_wordicwith|1->Monochrome|4->Color16|8->Color256|24->ColorRGB|32->ColorRGBA|n->failwith("invalid colors number : "^string_of_intn)(*
#define BI_RGB 0L
#define BI_RLE8 1L
#define BI_RLE4 2L *)letread_compressionic=matchread_dwordicwith|0->BI_RGB|1->BI_RLE8|2->BI_RLE4|n->failwith("invalid compression mode : "^string_of_intn)(* Entries of color maps stored on disk have the following format
typedef struct tagRGBQUAD {
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
The RGBQUAD structure contains the following fields:
Field Description
rgbBlue Specifies the intensity of blue in the color.
rgbGreen Specifies the intensity of green in the color.
rgbRed Specifies the intensity of red in the color.
rgbReserved Is not used and must be set to zero.
When loading a bmp we simply skip the rgbReserved field. *)letload_rgbquadic=letb=read_byteicinletg=read_byteicinletr=read_byteicinlet_u=read_byteicin{b=b;g=g;r=r;}letload_bitmapfileheaderic=letbfType=read_wordicinifbfType<>19778(* BM *)thenfailwith"Invalid file tag";letbfSize=read_dwordicinletbfReserved1=read_wordicinletbfReserved2=read_wordicinletbfOffBits=read_dwordicin{bfType;bfSize;bfReserved1;bfReserved2;bfOffBits;}letload_bitmapinfoheaderic=try(* Found a tagBITMAPINFO *)letbiSize=read_dwordicinletbiWidth=read_dwordicinletbiHeight=read_dwordicinletbiPlanes=read_wordicinletbiBitCount=read_bit_counticinletbiCompression=read_compressionicinletbiSizeImage=read_dwordicinletbiXPelsPerMeter=read_dwordicinletbiYPelsPerMeter=read_dwordicinletbiClrUsed=read_dwordicinletbiClrImportant=read_dwordicin(* header = tagBITMAPINFOHEADER *){biSize;biWidth;biHeight;biPlanes;biBitCount;biCompression;biSizeImage;biXPelsPerMeter;biYPelsPerMeter;biClrUsed;biClrImportant;}with|(Failuresase)->prerr_endlines;raiseeletload_colorsbfh_bihic=(* Reading RGBQUADs *)(* If biClrUsed = 0 then the whole color range is used, else only *)(* the amount given by biClrUsed is effectively used in the bmp. *)(* But in any case, the size of the color map is stored in the file. *)letcmaplength=(* Color map starts from byte number 54, and ends at *)(* beginning of the bitmap (actual image data, i.e. pixels of the bmp).*)(* bfOffBits = offset from bfh to actual image data. *)(* 40 = sizeof (bfh), 14 = sizeof (bih). *)(* Hence color map length is bfOffBits - 54. *)(* Useful to load images with biClrUsed handled incorrectly *)(* In fact, some savers store the whole colormap, instead of *)(* the number of entries given by biClrUsed... *)(bfh.bfOffBits-54)/4inArray.initcmaplength(fun_i->load_rgbquadic)(* Loads image data when image has 8 bit depth *)letload_image8databihic=letbitmap=Bytes.create(bih.biWidth*bih.biHeight)inmatchbih.biCompressionwith|BI_RGB->(* No compression : lines are stored in reverse order *)(* 'bih.biWidth' is padded to be a multiple of 4 pixels (32 bits) *)letpad=((bih.biWidth+3)/4)*4in(* Reading *)fori=bih.biHeight-1downto0doletbitmapindex=ref(i*bih.biWidth)inforj=0topad-1doletc=Char.chr(read_byteic)inifj<bih.biWidththenbitmap<<!bitmapindex&c;incrbitmapindexdonedone;bitmap|BI_RLE8->(* Run-length encoded format for bitmaps with 8 bits per pixel *)(* Coordinates of the current point in the image *)letx=ref0inlety=ref0inletbitmapindex=ref(!x+(bih.biHeight-!y-1)*bih.biWidth)inwhile!y<bih.biHeightdomatchread_byteicwith(* Absolute mode, if second byte is between 03H and FFH.
Encoded mode, with escape code otherwise. *)|0->(* Escape codes mode *)beginmatchread_byteicwith|0->(* End of line code *)x:=0;incry;bitmapindex:=!x+(bih.biHeight-!y-1)*bih.biWidth|1->(* End of bitmap : force exit *)y:=bih.biHeight|2->(* Delta *)letc1=read_byteicinx:=!x+c1;letc2=read_byteiciny:=!y+c2;bitmapindex:=!x+(bih.biHeight-!y-1)*bih.biWidth|c->(* c should be between 03H and FFH *)(* Absolute mode:
c represents the number of bytes which follow,
each of which contains the color index of a single pixel. *)for_i=0toc-1doletc1=read_byteicinbitmap<<!bitmapindex&Char.chrc1;incrx;incrbitmapindexdone;(* Odd length run: read an extra pad byte *)ifcland1<>0thenskip_byteicend|c->(* Encoded mode *)letc1=read_byteicinfor_i=0toc-1dobitmap<<!bitmapindex&Char.chrc1;incrx;incrbitmapindexdonedone;bitmap|BI_RLE4->failwith("Invalid compression mode : BI_RLE4")letload_image1databihic=letbitmap=Bytes.create(bih.biWidth*bih.biHeight)inletc=ref0in(* each scan line 'w', is padded to be a multiple of 32 *)letpad=((bih.biWidth+31)/32)*32infori=bih.biHeight-1downto0doletbitmapindex=ref(i*bih.biWidth)inletbnum=ref0inforj=0topad-1doif!bnumland7=0thenbeginc:=read_byteic;bnum:=0;end;ifj<bih.biWidththenbeginbitmap<<!bitmapindex&if!cland0x80<>0then'\001'else'\000';incrbitmapindex;c:=!clsl1;end;incrbnumdonedone;bitmapletload_image4databihic=letbitmap=Bytes.create(bih.biWidth*bih.biHeight)inmatchbih.biCompressionwith|BI_RGB->(* 'w' is padded to be a multiple of 8 pixels (32 bits) *)letpad=((bih.biWidth+7)/8)*8inletc=ref0infori=bih.biHeight-1downto0doletbitmapindex=ref(i*bih.biWidth)inletnyblenum=ref0inforj=0topad-1doif!nyblenumland1=0thenbegin(* Read the next byte *)c:=read_byteic;nyblenum:=0end;ifj<bih.biWidththenbeginbitmap<<!bitmapindex&Char.chr((!cland0xf0)lsr4);incrbitmapindex;c:=!clsl4end;incrnyblenumdonedone;bitmap|BI_RLE4->letx=ref0inlety=ref0inletbitmapindex=ref(!x+(bih.biHeight-!y-1)*bih.biWidth)inletc1=ref0inwhile!y<bih.biHeightdomatchread_byteicwith|0->(* Escape codes *)beginmatchread_byteicwith|0->(* End of line *)x:=0;incry;bitmapindex:=!x+(bih.biHeight-!y-1)*bih.biWidth|1->(* End of bitmap : force exit *)y:=bih.biHeight|2->(* Delta *)letc'=read_byteicinx:=!x+c';letc''=read_byteiciny:=!y+c'';bitmapindex:=!x+(bih.biHeight-!y-1)*bih.biWidth|c->(* Absolute mode *)fori=0toc-1doifiland1=0thenc1:=read_byteic;letc=ifiland1<>0then!c1else!c1lsr4inbitmap<<!bitmapindex&Char.chr(cland0x0F);incrx;incrbitmapindexdone;(* Read pad byte *)ifcland3=1||cland3=2thenskip_byteicend|c->(* Encoded mode *)letc1=read_byteicinletcol1=c1land0x0Fandcol2=(c1lsr4)land0x0Finfori=0toc-1doletc=ifiland1<>0thencol1elsecol2inbitmap<<!bitmapindex&Char.chrc;incrx;incrbitmapindexdonedone;bitmap|BI_RLE8->failwith("Invalid compression mode : BI_RLE8")letload_image24databihic=(* Bitmap is a bytes of RGB bytes *)letbitmap=Bytes.create((bih.biWidth*bih.biHeight)*3)inletpad=(4-((bih.biWidth*3)mod4))land0x03inletpp=ref0infori=bih.biHeight-1downto0dopp:=(i*bih.biWidth*3);for_j=0tobih.biWidth-1dobitmap<<!pp+2&Char.chr(read_byteic);(* Blue *)bitmap<<!pp+1&Char.chr(read_byteic);(* Green *)bitmap<<!pp&Char.chr(read_byteic);(* Red *)pp:=!pp+3done;for_j=0topad-1doskip_byteicdone;done;bitmapletload_image32databihic=(* Bitmap is a bytes of RGB bytes *)letbitmap=Bytes.create((bih.biWidth*bih.biHeight)*4)in(*
let pad = (4 - ((bih.biWidth * 4) mod 4)) land 0x03 in
let pad = 1 in
*)letpp=ref0infori=bih.biHeight-1downto0dopp:=(i*bih.biWidth*4);for_j=0tobih.biWidth-1dobitmap<<!pp+2&Char.chr(read_byteic);(* Blue *)bitmap<<!pp+1&Char.chr(read_byteic);(* Green *)bitmap<<!pp+0&Char.chr(read_byteic);(* Red *)bitmap<<!pp+3&Char.chr(read_byteic);(* Alpha *)pp:=!pp+4done;(*
for j = 0 to pad - 1 do skip_byte ic done;
*)done;bitmapletload_imagedatabihic=(* The bits in the array are packed together, but each scan line *)(* must be zero-padded to end on a LONG boundary. *)matchbih.biBitCountwith|Monochrome->load_image1databihic|Color16->load_image4databihic|Color256->load_image8databihic|ColorRGB->load_image24databihic|ColorRGBA->load_image32databihicletskip_toicn=while!bytes_read<>ndoskip_byteicdoneletcheck_headerfname=letic=open_in_binfnameinbytes_read:=0;trylet_bfh=load_bitmapfileheadericinletbih=load_bitmapinfoheadericinclose_inic;{header_width=bih.biWidth;header_height=bih.biHeight;header_infos=[];}with|_->close_inic;raiseWrong_file_typeletread_bmpic=bytes_read:=0;letbfh=load_bitmapfileheadericinletbih=load_bitmapinfoheadericinletcolormap=load_colorsbfhbihicinskip_toicbfh.bfOffBits;letbitmap=load_imagedatabihicin{bmpFileHeader=bfh;bmpInfoHeader=bih;bmpRgbQuad=colormap;bmpBytes=bitmap;}letread_bmp_filefname=letic=open_in_binfnameinletbmp=read_bmpicinclose_inic;bmpletimage_of_bmp=function{bmpFileHeader=_bfh;bmpInfoHeader=bih;bmpRgbQuad=colormap;bmpBytes=bitmap;}->matchbih.biBitCountwith|ColorRGB->Rgb24(Rgb24.create_withbih.biWidthbih.biHeight[]bitmap)|ColorRGBA->Rgba32(Rgba32.create_withbih.biWidthbih.biHeight[]bitmap)|Monochrome|Color16|Color256->Index8(Index8.create_withbih.biWidthbih.biHeight[]{map=colormap;max=256;}(-1)bitmap)letloadfname_opts=image_of_bmp(read_bmp_filefname)(* =================================================================== *)(* ============= Writting images as bmp files. ======================= *)(* =================================================================== *)letbytes_written=ref0letwrite_byteocb=incrbytes_written;output_byteocbletoutput_wordocw=(* little-endian form *)letb0=wland255inletb1=(wlsr8)land255inoutput_byteocb0;output_byteocb1letwrite_wordocw=output_wordocw;bytes_written:=!bytes_written+2letoutput_dwordocdw=(* little-endian form *)letb0=dwland255inletb1=(dwlsr8)land255inletb2=(dwlsr16)land255inletb3=(dwlsr24)land255inoutput_byteocb0;output_byteocb1;output_byteocb2;output_byteocb3letwrite_dwordocdw=output_dwordocdw;bytes_written:=!bytes_written+4letwrite_bit_countocbc=letbyte=matchbcwith|Monochrome->1|Color16->4|Color256->8|ColorRGB->24|ColorRGBA->32inwrite_wordocbyteletwrite_compressionocc=letdword=matchcwith|BI_RGB->0|BI_RLE8->1|BI_RLE4->2inwrite_dwordocdwordletwrite_rgbquadocrgb=letb=rgb.binletg=rgb.ginletr=rgb.rinletu=0inwrite_byteocb;write_byteocg;write_byteocr;write_byteoculetwrite_bmpFileHeaderoc=function{(* WORD *)bfType=bft;(* DWORD *)bfSize=bfs;(* WORD *)bfReserved1=bfr1;(* WORD *)bfReserved2=bfr2;(* DWORD *)bfOffBits=bfob}->letstart_index=!bytes_writteninwrite_wordocbft;letbfSize_index=!bytes_writteninwrite_dwordocbfs;write_wordocbfr1;write_wordocbfr2;letbfOffBits_index=!bytes_writteninwrite_dwordocbfob;letend_bmpFileHeader=!bytes_writteninstart_index,bfSize_index,bfOffBits_index,end_bmpFileHeaderletwrite_bmpInfoHeaderoc=function{(* DWORD *)biSize=bis;(* DWORD *)biWidth=biw;(* DWORD *)biHeight=bih;(* WORD *)biPlanes=bip;(* WORD *)biBitCount=bibc;(* DWORD *)biCompression=bic;(* DWORD *)biSizeImage=bisi;(* DWORD *)biXPelsPerMeter=bixpm;(* DWORD *)biYPelsPerMeter=biypm;(* DWORD *)biClrUsed=bicu;(* DWORD *)biClrImportant=bici}->letbiSize_index=!bytes_writteninwrite_dwordocbis;write_dwordocbiw;write_dwordocbih;write_wordocbip;write_bit_countocbibc;write_compressionocbic;letbiSizeImage_index=!bytes_writteninwrite_dwordocbisi;write_dwordocbixpm;write_dwordocbiypm;write_dwordocbicu;write_dwordocbici;letend_bmpInfoHeader=!bytes_writteninbiSize_index,biSizeImage_index,end_bmpInfoHeaderletwrite_colorsoccolor_map=(* If color_map is empty, should output a NULL character *)ifArray.lengthcolor_map=0thenwrite_byteoc0(* Otherwise write the rgb colors of the colormap *)elseArray.iter(write_rgbquadoc)color_map(* To denote the end of a scan line *)letwrite_end_of_scan_lineoc=write_byteoc0;write_byteoc0(* To denote the end of the bitmap *)letwrite_end_of_bitmapoc=write_byteoc0;write_byteoc1(* Writing padding bytes. *)letwrite_padocn=for_i=0ton-1dowrite_byteoc0done(* Run length encoding: write the number n of pixels encoded *)(* the color number given by color index c *)letrecwrite_rle_codeocnc=ifn<=255thenbeginwrite_byteocn;write_byteoccendelsebeginwrite_rle_codeoc255c;write_rle_codeoc(n-255)cendletwrite_rleocnchar=write_rle_codeocn(Char.codechar)(* In biRLE4 encoded mode the color byte is interpreted as two 4 bits
colors to alternatively write even and odd pixels.
Color is a char with 4 significant bytes.
We duplicate them to get 2 identical colors, for run-length encoding. *)letwrite_rle4ocnchar=letcode=Char.codecharinwrite_rle_codeocn(codelsl4+code)(* (4 - (n mod 4)) mod 4 *)letpad_bytesn=(4-(nmod4))land0x03letwrite_image1databmpoc=letbih=bmp.bmpInfoHeaderinifbih.biCompression<>BI_RGBthenfailwith"invalid compression for a monochrome bitmap"elseletstart_bitmap_index=!bytes_writteninletbitmap=bmp.bmpBytesinletwidth=bih.biWidthinletheight=bih.biHeightinletextra_padding_bytes=pad_bytes((width+7)/8)infori=height-1downto0do(* For each pixel in the line *)letstart=i*widthinletlim=(i+1)*width-1inletrecwrite_linexcountaccu=ifcount=8thenbeginwrite_byteocaccu;ifx<=limthenwrite_linex00endelseletchunk=(bitmap@%x)lsl(7-count)inletnew_accu=chunk+accuinifx=limthenwrite_byteocnew_accuelsewrite_line(x+1)(count+1)new_accuinwrite_linestart00;(* No end of scan line in bi_RGB mode *)(* Padding *)write_padocextra_padding_bytes;done;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_indexletwrite_image24databmpoc=letbih=bmp.bmpInfoHeaderinifbih.biCompression<>BI_RGBthenfailwith"invalid compression for a rgb bitmap"elseletstart_bitmap_index=!bytes_writteninletbitmap=bmp.bmpBytesinletwidth=bih.biWidthinletheight=bih.biHeightinletextra_padding_bytes=pad_bytes(width*3)infori=height-1downto0do(* For each pixel in the line *)letstart=i*width*3inletlim=(i+1)*width*3-1inletrecwrite_linex=write_byteoc(bitmap@%x+2);(* Blue *)write_byteoc(bitmap@%x+1);(* Green *)write_byteoc(bitmap@%x);(* Red *)letnew_x=x+3inifnew_x<limthenwrite_linenew_xinwrite_linestart;(* No end of scan line in bi_RGB mode *)(* Padding *)write_padocextra_padding_bytes;done;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_indexletwrite_image32databmpoc=letbih=bmp.bmpInfoHeaderinifbih.biCompression<>BI_RGBthenfailwith"invalid compression for a rgba bitmap"elseletstart_bitmap_index=!bytes_writteninletbitmap=bmp.bmpBytesinletwidth=bih.biWidthinletheight=bih.biHeightin(*
let extra_padding_bytes = pad_bytes (width * 4) in
*)fori=height-1downto0do(* For each pixel in the line *)letstart=i*width*3inletlim=(i+1)*width*4-1inletrecwrite_linex=write_byteoc(bitmap@%x+3);(* Alpha *)write_byteoc(bitmap@%x+2);(* Blue *)write_byteoc(bitmap@%x+1);(* Green *)write_byteoc(bitmap@%x);(* Red *)letnew_x=x+4inifnew_x<limthenwrite_linenew_xinwrite_linestart;(* No end of scan line in bi_RGB mode *)(*
(* Padding *)
write_pad oc extra_padding_bytes;
*)done;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_indexletwrite_image4databmpoc=letbih=bmp.bmpInfoHeaderinletstart_bitmap_index=!bytes_writteninletbitmap=bmp.bmpBytesinletwidth=bih.biWidthinletheight=bih.biHeightinmatchbih.biCompressionwith|BI_RGB->(* 'w' is padded to be a multiple of 8 pixels (32 bits) *)letextra_padding_bytes=pad_bytes((width+1)/2)infori=height-1downto0do(* For each pixel in the line *)letstart=i*widthinletlim=(i+1)*width-1inletrecwrite_linexcountaccu=ifcount=2thenbeginwrite_byteocaccu;ifx<=limthenwrite_linex00endelseletchunk=(bitmap@%x)lsl(4-count)inletnew_accu=chunk+accuinifx=limthenwrite_byteocnew_accuelsewrite_line(x+1)(count+1)new_accuinwrite_linestart00;(* Padding *)write_padocextra_padding_bytes;done;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_index|BI_RLE4->(* We compress in encoded mode, not in absolute mode. *)(* So we do not have to align each run. *)(* However, each scan line is padded to be a multiple of 8 *)(* pixels (32 bits) *)(* For each line *)fori=height-1downto0do(* For each pixel in the line *)letstart=i*widthinletlim=(i+1)*width-1inletrecwrite_linexcountpred=letcur=Bytes.getbitmapxinifcur=predthenifx=limthenwrite_rle4oc(count+1)predelsewrite_line(x+1)(count+1)predelsebeginwrite_rle4occountpred;ifx=limthenwrite_rle4oc1curelsewrite_line(x+1)1curendinwrite_linestart0(Bytes.getbitmapstart);write_end_of_scan_lineoc;(* No padding in this mode *)done;write_end_of_bitmapoc;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_index|BI_RLE8->failwith("Invalid compression mode : BI_RLE8")letwrite_image8databmpoc=letbih=bmp.bmpInfoHeaderinletstart_bitmap_index=!bytes_writteninletbitmap=bmp.bmpBytesinletwidth=bih.biWidthinletheight=bih.biHeightinmatchbih.biCompressionwith|BI_RGB->(* 'w' is padded to be a multiple of 8 pixels (32 bits) *)letextra_padding_bytes=pad_byteswidthinfori=height-1downto0do(* For each pixel in the line *)letstart=i*widthinletlim=(i+1)*width-1inletrecwrite_linex=write_byteoc(bitmap@%x);ifx<limthenwrite_line(x+1)inwrite_linestart;(* Padding *)write_padocextra_padding_bytes;done;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_index|BI_RLE8->(* We compress in encoded mode, not in absolute mode. *)(* So we do not have to align each run. *)(* However, each scan line is padded to be a multiple of 8 *)(* pixels (32 bits) *)(* For each line *)fori=height-1downto0do(* For each pixel in the line *)letstart=i*widthinletlim=(i+1)*width-1inletrecwrite_linexcountpred=letcur=Bytes.getbitmapxinifcur=predthenifx=limthenwrite_rleoc(count+1)predelsewrite_line(x+1)(count+1)predelsebeginwrite_rleoccountpred;ifx=limthenwrite_rleoc1curelsewrite_line(x+1)1curendinwrite_linestart0(Bytes.getbitmapstart);write_end_of_scan_lineoc;(* No padding in this mode *)done;write_end_of_bitmapoc;letend_bitmap_index=!bytes_writteninstart_bitmap_index,end_bitmap_index|BI_RLE4->failwith("Invalid compression mode : BI_RLE8")letwrite_image_dataocbmp=letbih=bmp.bmpInfoHeaderinmatchbih.biBitCountwith|Monochrome->write_image1databmpoc|Color16->write_image4databmpoc|Color256->write_image8databmpoc|ColorRGB->write_image24databmpoc|ColorRGBA->write_image32databmpocletbmp_of_imageimg=matchimgwith|Rgb24bitmap->letbiW=bitmap.Rgb24.widthandbiH=bitmap.Rgb24.heightanddata=Rgb24.dumpbitmapinletbfh={(* WORD *)bfType=19778(* BM *);(* DWORD *)bfSize=-1(* Unknown to be updated *);(* WORD *)bfReserved1=0;(* WORD *)bfReserved2=0;(* DWORD *)bfOffBits=-1(* Unknown to be updated *)}inletbih={(* The size in bytes of this header. *)biSize=-1;(* Unknown to be updated *)(* Width and height of the image *)biWidth=biW;biHeight=biH;(* According to the format, Must be set to 1. *)biPlanes=1;(* 24 bits pixels. *)biBitCount=ColorRGB;(* Compression is no compression: we output pixels as
rgb rgb ... with padding. *)biCompression=BI_RGB;(* The size of the actual image pixels representation in the
file. Due to padding, cannot be computed here. *)biSizeImage=-1(* Unknown to be updated *);(* This should be OK *)biXPelsPerMeter=600;biYPelsPerMeter=600;(* Unknown: the number of colors actually
used by the image. Must be computed while writing the
image. *)biClrUsed=0;(* Number of important colors. If 0, all colors are important *)biClrImportant=0}in{bmpFileHeader=bfh;bmpInfoHeader=bih;bmpRgbQuad=[||];bmpBytes=data}|Rgba32bitmap->letbiW=bitmap.Rgba32.widthandbiH=bitmap.Rgba32.heightanddata=Rgba32.dumpbitmapinletbfh={(* WORD *)bfType=19778(* BM *);(* DWORD *)bfSize=-1(* Unknown to be updated *);(* WORD *)bfReserved1=0;(* WORD *)bfReserved2=0;(* DWORD *)bfOffBits=-1(* Unknown to be updated *)}inletbih={(* The size in bytes of this header. *)biSize=-1;(* Unknown to be updated *)(* Width and height of the image *)biWidth=biW;biHeight=biH;(* According to the format, Must be set to 1. *)biPlanes=1;(* 24 bits pixels. *)biBitCount=ColorRGBA;(* Compression is no compression: we output pixels as
rgb rgb ... with padding. *)biCompression=BI_RGB;(* The size of the actual image pixels representation in the
file. Due to padding, cannot be computed here. *)biSizeImage=-1(* Unknown to be updated *);(* This should be OK *)biXPelsPerMeter=600;biYPelsPerMeter=600;(* Unknown: the number of colors actually
used by the image. Must be computed while writing the
image. *)biClrUsed=0;(* Number of important colors. If 0, all colors are important *)biClrImportant=0}in{bmpFileHeader=bfh;bmpInfoHeader=bih;bmpRgbQuad=[||];bmpBytes=data}|Index8bitmap->letcolormap=bitmap.Index8.colormap.mapandbiW=bitmap.Index8.widthandbiH=bitmap.Index8.heightanddata=Index8.dumpbitmapinletbfh={(* WORD *)bfType=19778(* BM *);(* DWORD *)bfSize=-1(* Unknown to be updated *);(* WORD *)bfReserved1=0;(* WORD *)bfReserved2=0;(* DWORD *)bfOffBits=-1(* Unknown to be updated *)}inletbiBitCount,biClrUsed,biCompression,biClrImportant=letcol_map_len=Array.lengthcolormapinmatchcol_map_lenwith|nwhenn<=2->Monochrome,2,BI_RGB,2|16->Color16,col_map_len,BI_RGB,0|nwhenn<=16->Color16,col_map_len,BI_RLE4,0|256->Color256,col_map_len,BI_RGB,0|nwhenn<=256->Color256,col_map_len,BI_RLE8,0|_n->failwith"Too many colors for a bitmap with 8 bits per pixel"inletbih={biSize=-1;biWidth=biW;biHeight=biH;biPlanes=1;biBitCount=biBitCount;biCompression=biCompression;biSizeImage=-1;biXPelsPerMeter=600;biYPelsPerMeter=600;biClrUsed=biClrUsed;biClrImportant=biClrImportant;}in{bmpFileHeader=bfh;bmpInfoHeader=bih;bmpRgbQuad=colormap;bmpBytes=data;}|_->raiseWrong_image_typeletwrite_bmpoc=function{bmpFileHeader=bmpFileHeader;bmpInfoHeader=bmpInfoHeader;bmpRgbQuad=colormap;bmpBytes=_bitmap}asbmp->bytes_written:=0;letstart_index,bfSize_index,bfOffBits_index,end_bmpFileHeader=write_bmpFileHeaderocbmpFileHeaderinletstart_bmpInfoHeader=end_bmpFileHeaderinletbiSize_index,biSizeImage_index,end_bmpInfoHeader=write_bmpInfoHeaderocbmpInfoHeaderinwrite_colorsoccolormap;letstart_bitmap_index,end_bitmap_index=write_image_dataocbmpin(* Correcting sizes: bfSize, bfOffBits, biSize, bisizeImage *)letbfSize=(* Given in bytes! not in DWORDs *)!bytes_written-start_indexinseek_outocbfSize_index;output_dwordocbfSize;letbfOffBits=(* Given in bytes *)start_bitmap_index-start_indexinseek_outocbfOffBits_index;output_dwordocbfOffBits;letbiSize=(* Given in bytes *)end_bmpInfoHeader-start_bmpInfoHeaderinseek_outocbiSize_index;output_dwordocbiSize;letbiSizeImage=(* Given in bytes *)end_bitmap_index-start_bitmap_indexinseek_outocbiSizeImage_index;output_dwordocbiSizeImageletwrite_bmp_filefnamebmp=letoc=open_out_binfnameinwrite_bmpocbmp;close_outocletsavefname_optsimg=write_bmp_filefname(bmp_of_imageimg)let()=add_methodsBmp{check_header=check_header;load=Someload;save=Somesave;load_sequence=None;save_sequence=None;}letsave_bmp=write_bmp_fileandload_bmp=read_bmp_file