387 lines
9.1 KiB
HolyC
387 lines
9.1 KiB
HolyC
#help_index "Graphics/Windows BMP Files"
|
|
|
|
#define BMP_COLORS_NUM 16
|
|
|
|
class CFileBMP
|
|
{
|
|
U16 type;
|
|
U32 file_size;
|
|
U32 reserved;
|
|
U32 data_offset;
|
|
|
|
U32 header_size;
|
|
U32 width;
|
|
U32 height;
|
|
U16 planes;
|
|
U16 bit_cnt;
|
|
U32 compression;
|
|
U32 image_size;
|
|
U32 x_pixs_per_meter;
|
|
U32 y_pixs_per_meter;
|
|
U32 colors_used;
|
|
U32 important_colors;
|
|
|
|
U0 end;
|
|
|
|
CBGR24 palette[BMP_COLORS_NUM];
|
|
};
|
|
|
|
public CFileBMP *BMP4To(CDC *dc)
|
|
{//To Windows 4-bit BMP.
|
|
U8 *src,*ptr;
|
|
CBGR48 palette[COLORS_NUM];
|
|
I64 i,x,y,w=dc->width>>1,
|
|
size=sizeof(CFileBMP)+dc->width*dc->height>>1;
|
|
CFileBMP *bmp =CAlloc(size);
|
|
bmp->type ='BM';
|
|
bmp->planes =1;
|
|
bmp->file_size=size;
|
|
bmp->data_offset=sizeof(CFileBMP);
|
|
bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size);
|
|
bmp->width =dc->width;
|
|
bmp->height =dc->height;
|
|
bmp->bit_cnt =4;
|
|
bmp->image_size=dc->width*dc->height>>1;
|
|
GrPaletteGet(palette);
|
|
#assert COLORS_NUM==BMP_COLORS_NUM
|
|
for (i=0;i<BMP_COLORS_NUM;i++) {
|
|
bmp->palette[i].b=palette[i].b>>8;
|
|
bmp->palette[i].g=palette[i].g>>8;
|
|
bmp->palette[i].r=palette[i].r>>8;
|
|
bmp->palette[i].pad=0;
|
|
}
|
|
ptr=bmp(U8 *)+bmp->data_offset;
|
|
for (y=dc->height-1;y>=0;y--) {
|
|
src=y*dc->width_internal+dc->body;
|
|
for (x=0;x<w;x++) {
|
|
*ptr|=(*src++&15)<<4;
|
|
*ptr|=*src++&15;
|
|
ptr++;
|
|
}
|
|
}
|
|
return bmp;
|
|
}
|
|
|
|
public CFileBMP *BMPRLE4To(CDC *dc)
|
|
{//To Windows RLE4 bit BMP.
|
|
U8 *src,*ptr,*start;
|
|
I64 i,x,y,w=dc->width,cnt,pattern;
|
|
CBGR48 palette[COLORS_NUM];
|
|
CFileBMP *bmp =CAlloc(sizeof(CFileBMP)+2*(dc->width+1)*dc->height);
|
|
bmp->type ='BM';
|
|
bmp->planes =1;
|
|
bmp->data_offset=sizeof(CFileBMP);
|
|
bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size);
|
|
bmp->width =dc->width;
|
|
bmp->height =dc->height;
|
|
bmp->bit_cnt =4;
|
|
bmp->compression=2; //RLE4
|
|
GrPaletteGet(palette);
|
|
#assert COLORS_NUM==BMP_COLORS_NUM
|
|
for (i=0;i<BMP_COLORS_NUM;i++) {
|
|
bmp->palette[i].b=palette[i].b>>8;
|
|
bmp->palette[i].g=palette[i].g>>8;
|
|
bmp->palette[i].r=palette[i].r>>8;
|
|
bmp->palette[i].pad=0;
|
|
}
|
|
start=ptr=bmp(U8 *)+bmp->data_offset;
|
|
for (y=dc->height-1;y>=0;y--) {
|
|
src=y*dc->width_internal+dc->body;
|
|
x=0;
|
|
while (x<w) {
|
|
pattern=(src[0]&15)<<4+src[1]&15;
|
|
if (x+1<w && src[0]&15==src[1]&15) {
|
|
src+=2;
|
|
cnt=2;
|
|
x+=2;
|
|
while (x<w && cnt<U8_MAX) {
|
|
if (*src&15==pattern&15) {
|
|
src++;
|
|
cnt++;
|
|
x++;
|
|
} else
|
|
break;
|
|
}
|
|
} else {
|
|
src+=2;
|
|
if (x+1<w)
|
|
cnt=2;
|
|
else
|
|
cnt=1;
|
|
x+=2;
|
|
}
|
|
*ptr++=cnt;
|
|
*ptr++=pattern;
|
|
}
|
|
*ptr(U16 *)++=0;
|
|
}
|
|
bmp->image_size=ptr-start;
|
|
bmp->file_size=sizeof(CFileBMP)+bmp->image_size;
|
|
return bmp;
|
|
}
|
|
|
|
public CFileBMP *BMP24To(CDC *dc)
|
|
{//To Windows 24-bit BMP.
|
|
U8 *src;
|
|
I64 i,x,y,size=offset(CFileBMP.end)+dc->width*dc->height*sizeof(CBGR24);
|
|
CBGR24 *bgr;
|
|
CFileBMP *bmp =CAlloc(size);
|
|
bmp->type ='BM';
|
|
bmp->planes =1;
|
|
bmp->file_size=size;
|
|
bmp->data_offset=offset(CFileBMP.end);
|
|
bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size);
|
|
bmp->width =dc->width;
|
|
bmp->height =dc->height;
|
|
bmp->bit_cnt =32;
|
|
bmp->image_size=dc->width*dc->height<<2;
|
|
|
|
bgr=bmp(U8 *)+bmp->data_offset;
|
|
for (y=dc->height-1;y>=0;y--) {
|
|
src=y*dc->width_internal+dc->body;
|
|
for (x=0;x<dc->width;x++) {
|
|
i=*src++;
|
|
if (i&BLUE) bgr->b=0x7F;
|
|
if (i&GREEN) bgr->g=0x7F;
|
|
if (i&RED) bgr->r=0x7F;
|
|
if (i&8) {
|
|
if (bgr->b) bgr->b=0xFF;
|
|
if (bgr->g) bgr->g=0xFF;
|
|
if (bgr->r) bgr->r=0xFF;
|
|
}
|
|
bgr(U8 *)+=4;
|
|
}
|
|
}
|
|
return bmp;
|
|
}
|
|
|
|
public I64 BMPWrite(U8 *filename,CDC *dc,I64 bits=4)
|
|
{//Window's BMP Files.
|
|
I64 size;
|
|
CFileBMP *bmp;
|
|
if (bits==4) {
|
|
if (IsDotZ(filename)) //.Z compression is better than RLE
|
|
bmp=BMP4To(dc);
|
|
else {
|
|
bmp=BMPRLE4To(dc);
|
|
if (bmp->file_size>sizeof(CFileBMP)+dc->width*dc->height>>1) {
|
|
Free(bmp);
|
|
bmp=BMP4To(dc);
|
|
}
|
|
}
|
|
} else if (bits==24)
|
|
bmp=BMP24To(dc);
|
|
else {
|
|
"Format Not Supported.\n";
|
|
return 0;
|
|
}
|
|
size=bmp->file_size;
|
|
FileWrite(filename,bmp,bmp->file_size);
|
|
Free(bmp);
|
|
return size;
|
|
}
|
|
|
|
U8 *BMPPaletteNew(CFileBMP *bmp)
|
|
{
|
|
I64 i,j,best,score,best_score;
|
|
CBGR48 palette[COLORS_NUM];
|
|
U8 *res=CAlloc(BMP_COLORS_NUM*sizeof(U8));
|
|
GrPaletteGet(palette);
|
|
#assert COLORS_NUM==BMP_COLORS_NUM
|
|
for (i=0;i<BMP_COLORS_NUM;i++) {
|
|
best=i;
|
|
best_score=I64_MAX;
|
|
for (j=0;j<BMP_COLORS_NUM;j++) {
|
|
score=SqrI64(bmp->palette[i].r-palette[j].r>>8)+
|
|
SqrI64(bmp->palette[i].g-palette[j].g>>8)+
|
|
SqrI64(bmp->palette[i].b-palette[j].b>>8);
|
|
if (score<best_score) {
|
|
best=j;
|
|
best_score=score;
|
|
}
|
|
}
|
|
res[i]=best;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
U8 ms_paint_palette[BMP_COLORS_NUM]={0,4,2,6,1,5,3,8,7,12,10,14,9,13,11,15};
|
|
|
|
I64 BMP24Color(CBGR24 *ptr,Bool dither_probability)
|
|
{
|
|
I64 res,k;
|
|
if (dither_probability) {
|
|
k=RandU32;
|
|
if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=3*SqrI64(k.u8[0]))
|
|
res=8;
|
|
else
|
|
res=0;
|
|
if (ptr->r>=k.u8[1]) res|=RED;
|
|
if (ptr->g>=k.u8[2]) res|=GREEN;
|
|
if (ptr->b>=k.u8[3]) res|=BLUE;
|
|
} else {
|
|
if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=SqrI64(0x80)) {
|
|
res=8;
|
|
if (ptr->r>=0x80) res|=RED;
|
|
if (ptr->g>=0x80) res|=GREEN;
|
|
if (ptr->b>=0x80) res|=BLUE;
|
|
} else {
|
|
res=0;
|
|
if (ptr->r>=0x40) res|=RED;
|
|
if (ptr->g>=0x40) res|=GREEN;
|
|
if (ptr->b>=0x40) res|=BLUE;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
public CDC *BMPRead(U8 *filename,Bool dither_probability=FALSE,
|
|
Bool use_ms_paint_palette=FALSE)
|
|
{//Window's BMP Files.
|
|
I64 i,j,cnt;
|
|
U8 *palette_map,*ptr;
|
|
Bool rle;
|
|
CFileBMP *bmp;
|
|
CDC *res=NULL;
|
|
if (ptr=FileRead(filename)) {
|
|
bmp=ptr;
|
|
if (0<bmp->width<I32_MAX && 0<bmp->height<I32_MAX) {
|
|
res=DCNew(bmp->width,bmp->height);
|
|
ptr+=bmp->data_offset;
|
|
if (bmp->compression==2)
|
|
rle=TRUE;
|
|
else
|
|
rle=FALSE;
|
|
if (use_ms_paint_palette)
|
|
palette_map=ms_paint_palette;
|
|
else
|
|
palette_map=BMPPaletteNew(bmp);
|
|
if (bmp->bit_cnt==4) {
|
|
for (i=bmp->height-1;i>=0;i--)
|
|
if (rle) {//We don't support full RLE4, just our own subset
|
|
j=0;
|
|
while (cnt=*ptr++) {
|
|
if (cnt==1) {
|
|
res->color=palette_map[*ptr++&15];
|
|
GrPlot(res,j++,i);
|
|
} else {
|
|
if (cnt==2 && *ptr>>4!=*ptr&15) {
|
|
res->color=palette_map[*ptr&15];
|
|
GrPlot(res,j+1,i);
|
|
res->color=palette_map[*ptr>>4];
|
|
GrPlot(res,j,i);
|
|
ptr++;
|
|
j+=2;
|
|
} else {
|
|
res->color=palette_map[*ptr++&15];
|
|
while (cnt--)
|
|
GrPlot(res,j++,i);
|
|
}
|
|
}
|
|
}
|
|
ptr++;
|
|
} else
|
|
for (j=0;j<(bmp->width+7)&~7;) {
|
|
res->color=palette_map[*ptr&15];
|
|
GrPlot(res,j+1,i);
|
|
res->color=palette_map[*ptr>>4];
|
|
GrPlot(res,j,i);
|
|
ptr++;
|
|
j+=2;
|
|
}
|
|
if (!use_ms_paint_palette)
|
|
Free(palette_map);
|
|
} else if (bmp->bit_cnt==24) {
|
|
for (i=bmp->height-1;i>=0;i--) {
|
|
for (j=0;j<bmp->width;j++,ptr+=3) {
|
|
res->color=BMP24Color(ptr,dither_probability);
|
|
GrPlot(res,j,i);
|
|
}
|
|
ptr+=bmp->width&3;
|
|
}
|
|
if (!use_ms_paint_palette)
|
|
Free(palette_map);
|
|
} else if (bmp->bit_cnt>=32) {
|
|
for (i=bmp->height-1;i>=0;i--)
|
|
for (j=0;j<bmp->width;j++,ptr+=4) {
|
|
res->color=BMP24Color(ptr,dither_probability);
|
|
GrPlot(res,j,i);
|
|
}
|
|
if (!use_ms_paint_palette)
|
|
Free(palette_map);
|
|
} else {
|
|
"Format Not Supported.\n";
|
|
DCDel(res);
|
|
res=NULL;
|
|
}
|
|
} else
|
|
"Invalid BMP File\n";
|
|
Free(bmp);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
#help_index "Graphics/Sprite;Graphics/Windows BMP Files;"\
|
|
"DolDoc/Output;StdOut/DolDoc"
|
|
public U0 DocBMP(CDoc *doc=NULL,U8 *filename,Bool dither_probability=FALSE,
|
|
Bool use_ms_paint_palette=FALSE)
|
|
{//Put a BMP file into a document as a sprite.
|
|
CDC *dc=BMPRead(filename,dither_probability,use_ms_paint_palette);
|
|
U8 *elems=DC2Sprite(dc);
|
|
DocSprite(doc,elems);
|
|
Free(elems);
|
|
DCDel(dc);
|
|
}
|
|
|
|
#help_index "Graphics/Windows BMP Files;Graphics/Scrn"
|
|
public I64 BMPScrnCapture(U8 *filename,I64 bits=4,Bool include_zoom=TRUE)
|
|
{//Capture scrn as BMP file.
|
|
CDC *dc=DCScrnCapture(include_zoom);
|
|
I64 size=BMPWrite(filename,dc,bits);
|
|
DCDel(dc);
|
|
return size;
|
|
}
|
|
|
|
public I64 GR2BMPLst(U8 *files_find_mask,U8 *fu_flags=NULL,
|
|
U8 *out_print_fmt="~:/Tmp/VID%05d.BMP.Z",F64 fps=30000.0/1001)
|
|
{/*Cvt movie from GR lst to BMP lst
|
|
"+d" will delete GRLst files.
|
|
*/
|
|
I64 res=0,fuf_flags=0;
|
|
CDirEntry *tmpde,*tmpde1;
|
|
CDC *dc,*base=DCNew(GR_WIDTH,GR_HEIGHT);
|
|
U8 *st,*last_st;
|
|
CDate cdt=I64_MIN;
|
|
Bool old_silent;
|
|
ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),"+f+F");
|
|
ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),fu_flags);
|
|
tmpde=tmpde1=FilesFind(files_find_mask,fuf_flags&FUG_FILES_FIND);
|
|
last_st=MStrPrint(out_print_fmt,0);
|
|
progress1_max=LinkedLstCnt(tmpde);
|
|
while (tmpde) {
|
|
dc=GRRead(tmpde->full_name);
|
|
GrBlot(base,dc->x0,dc->y0,dc);
|
|
if (cdt==I64_MIN)
|
|
cdt=dc->cdt;
|
|
while (cdt<=dc->cdt) {
|
|
st=MStrPrint(out_print_fmt,res++);
|
|
BMPWrite(st,base);
|
|
Free(st);
|
|
cdt+=CDATE_FREQ/fps;
|
|
}
|
|
if (fuf_flags&FUF_DEL) {
|
|
old_silent=Silent;
|
|
Del(tmpde->full_name);
|
|
Silent(old_silent);
|
|
}
|
|
DCDel(dc);
|
|
progress1++;
|
|
tmpde=tmpde->next;
|
|
}
|
|
progress1=progress1_max=0;
|
|
DirTreeDel(tmpde1);
|
|
Free(last_st);
|
|
DCDel(base);
|
|
return res;
|
|
}
|