001 #help_index "Graphics/Windows BMP Files" 002 003 #define BMP_COLORS_NUM 16 004 005 class CFileBMP 006 { 007 U16 type; 008 U32 file_size; 009 U32 reserved; 010 U32 data_offset; 011 012 U32 header_size; 013 U32 width; 014 U32 height; 015 U16 planes; 016 U16 bit_cnt; 017 U32 compression; 018 U32 image_size; 019 U32 x_pixs_per_meter; 020 U32 y_pixs_per_meter; 021 U32 colors_used; 022 U32 important_colors; 023 024 U0 end; 025 026 CBGR24 palette[BMP_COLORS_NUM]; 027 }; 028 029 public CFileBMP *BMP4To(CDC *dc) 030 {//To Windows 4-bit BMP. 031 U8 *src,*ptr; 032 CBGR48 palette[COLORS_NUM]; 033 I64 i,x,y,w=dc->width>>1, 034 size=sizeof(CFileBMP)+dc->width*dc->height>>1; 035 CFileBMP *bmp =CAlloc(size); 036 bmp->type ='BM'; 037 bmp->planes =1; 038 bmp->file_size=size; 039 bmp->data_offset=sizeof(CFileBMP); 040 bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size); 041 bmp->width =dc->width; 042 bmp->height =dc->height; 043 bmp->bit_cnt =4; 044 bmp->image_size=dc->width*dc->height>>1; 045 GrPaletteGet(palette); 046 #assert COLORS_NUM==BMP_COLORS_NUM 047 for (i=0;i<BMP_COLORS_NUM;i++) { 048 bmp->palette[i].b=palette[i].b>>8; 049 bmp->palette[i].g=palette[i].g>>8; 050 bmp->palette[i].r=palette[i].r>>8; 051 bmp->palette[i].pad=0; 052 } 053 ptr=bmp(U8 *)+bmp->data_offset; 054 for (y=dc->height-1;y>=0;y--) { 055 src=y*dc->width_internal+dc->body; 056 for (x=0;x<w;x++) { 057 *ptr|=(*src++&15)<<4; 058 *ptr|=*src++&15; 059 ptr++; 060 } 061 } 062 return bmp; 063 } 064 065 public CFileBMP *BMPRLE4To(CDC *dc) 066 {//To Windows RLE4 bit BMP. 067 U8 *src,*ptr,*start; 068 I64 i,x,y,w=dc->width,cnt,pattern; 069 CBGR48 palette[COLORS_NUM]; 070 CFileBMP *bmp =CAlloc(sizeof(CFileBMP)+2*(dc->width+1)*dc->height); 071 bmp->type ='BM'; 072 bmp->planes =1; 073 bmp->data_offset=sizeof(CFileBMP); 074 bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size); 075 bmp->width =dc->width; 076 bmp->height =dc->height; 077 bmp->bit_cnt =4; 078 bmp->compression=2; //RLE4 079 GrPaletteGet(palette); 080 #assert COLORS_NUM==BMP_COLORS_NUM 081 for (i=0;i<BMP_COLORS_NUM;i++) { 082 bmp->palette[i].b=palette[i].b>>8; 083 bmp->palette[i].g=palette[i].g>>8; 084 bmp->palette[i].r=palette[i].r>>8; 085 bmp->palette[i].pad=0; 086 } 087 start=ptr=bmp(U8 *)+bmp->data_offset; 088 for (y=dc->height-1;y>=0;y--) { 089 src=y*dc->width_internal+dc->body; 090 x=0; 091 while (x<w) { 092 pattern=(src[0]&15)<<4+src[1]&15; 093 if (x+1<w && src[0]&15==src[1]&15) { 094 src+=2; 095 cnt=2; 096 x+=2; 097 while (x<w && cnt<U8_MAX) { 098 if (*src&15==pattern&15) { 099 src++; 100 cnt++; 101 x++; 102 } else 103 break; 104 } 105 } else { 106 src+=2; 107 if (x+1<w) 108 cnt=2; 109 else 110 cnt=1; 111 x+=2; 112 } 113 *ptr++=cnt; 114 *ptr++=pattern; 115 } 116 *ptr(U16 *)++=0; 117 } 118 bmp->image_size=ptr-start; 119 bmp->file_size=sizeof(CFileBMP)+bmp->image_size; 120 return bmp; 121 } 122 123 public CFileBMP *BMP24To(CDC *dc) 124 {//To Windows 24-bit BMP. 125 U8 *src; 126 I64 i,x,y,size=offset(CFileBMP.end)+dc->width*dc->height*sizeof(CBGR24); 127 CBGR24 *bgr; 128 CFileBMP *bmp =CAlloc(size); 129 bmp->type ='BM'; 130 bmp->planes =1; 131 bmp->file_size=size; 132 bmp->data_offset=offset(CFileBMP.end); 133 bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size); 134 bmp->width =dc->width; 135 bmp->height =dc->height; 136 bmp->bit_cnt =32; 137 bmp->image_size=dc->width*dc->height<<2; 138 139 bgr=bmp(U8 *)+bmp->data_offset; 140 for (y=dc->height-1;y>=0;y--) { 141 src=y*dc->width_internal+dc->body; 142 for (x=0;x<dc->width;x++) { 143 i=*src++; 144 if (i&BLUE) bgr->b=0x7F; 145 if (i&GREEN) bgr->g=0x7F; 146 if (i&RED) bgr->r=0x7F; 147 if (i&8) { 148 if (bgr->b) bgr->b=0xFF; 149 if (bgr->g) bgr->g=0xFF; 150 if (bgr->r) bgr->r=0xFF; 151 } 152 bgr(U8 *)+=4; 153 } 154 } 155 return bmp; 156 } 157 158 public I64 BMPWrite(U8 *filename,CDC *dc,I64 bits=4) 159 {//Window's BMP Files. 160 I64 size; 161 CFileBMP *bmp; 162 if (bits==4) { 163 if (IsDotZ(filename)) //.Z compression is better than RLE 164 bmp=BMP4To(dc); 165 else { 166 bmp=BMPRLE4To(dc); 167 } 168 } else if (bits==24) 169 bmp=BMP24To(dc); 170 else { 171 "Format Not Supported.\n"; 172 return 0; 173 } 174 FileWrite(filename,bmp,bmp->file_size); 175 Free(bmp); 176 return size; 177 } 178 179 U8 *BMPPaletteNew(CFileBMP *bmp) 180 { 181 I64 i,j,best,score,best_score; 182 CBGR48 palette[COLORS_NUM]; 183 U8 *res=CAlloc(BMP_COLORS_NUM*sizeof(U8)); 184 GrPaletteGet(palette); 185 #assert COLORS_NUM==BMP_COLORS_NUM 186 for (i=0;i<BMP_COLORS_NUM;i++) { 187 best=i; 188 best_score=I64_MAX; 189 for (j=0;j<BMP_COLORS_NUM;j++) { 190 score=SqrI64(bmp->palette[i].r-palette[j].r>>8)+ 191 SqrI64(bmp->palette[i].g-palette[j].g>>8)+ 192 SqrI64(bmp->palette[i].b-palette[j].b>>8); 193 if (score<best_score) { 194 best=j; 195 best_score=score; 196 } 197 } 198 res[i]=best; 199 } 200 return res; 201 } 202 203 U8 ms_paint_palette[BMP_COLORS_NUM]={0,4,2,6,1,5,3,8,7,12,10,14,9,13,11,15}; 204 205 206 I64 BMP24Color(CBGR24 *ptr,Bool dither_probability) 207 { 208 I64 res,k; 209 if (dither_probability) { 210 k=RandU32; 211 if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=3*SqrI64(k.u8[0])) 212 res=8; 213 else 214 res=0; 215 if (ptr->r>=k.u8[1]) res|=RED; 216 if (ptr->g>=k.u8[2]) res|=GREEN; 217 if (ptr->b>=k.u8[3]) res|=BLUE; 218 } else { 219 if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=SqrI64(0x80)) { 220 res=8; 221 if (ptr->r>=0x80) res|=RED; 222 if (ptr->g>=0x80) res|=GREEN; 223 if (ptr->b>=0x80) res|=BLUE; 224 } else { 225 res=0; 226 if (ptr->r>=0x40) res|=RED; 227 if (ptr->g>=0x40) res|=GREEN; 228 if (ptr->b>=0x40) res|=BLUE; 229 } 230 } 231 return res; 232 } 233 234 public CDC *BMPRead(U8 *filename,Bool dither_probability=FALSE, 235 Bool use_ms_paint_palette=FALSE) 236 {//Window's BMP Files. 237 I64 i,j,cnt; 238 U8 *palette_map,*ptr; 239 Bool rle; 240 CFileBMP *bmp; 241 CDC *res=NULL; 242 if (ptr=FileRead(filename)) { 243 bmp=ptr; 244 if (0<bmp->width<I32_MAX && 0<bmp->height<I32_MAX) { 245 res=DCNew(bmp->width,bmp->height); 246 ptr+=bmp->data_offset; 247 if (bmp->compression==2) 248 rle=TRUE; 249 else 250 rle=FALSE; 251 if (use_ms_paint_palette) 252 palette_map=ms_paint_palette; 253 else 254 palette_map=BMPPaletteNew(bmp); 255 if (bmp->bit_cnt==4) { 256 for (i=bmp->height-1;i>=0;i--) 257 if (rle) {//We don't support full RLE4, just our own subset 258 j=0; 259 while (cnt=*ptr++) { 260 if (cnt==1) { 261 res->color=palette_map[*ptr++&15]; 262 GrPlot(res,j++,i); 263 } else { 264 if (cnt==2 && *ptr>>4!=*ptr&15) { 265 res->color=palette_map[*ptr&15]; 266 GrPlot(res,j+1,i); 267 res->color=palette_map[*ptr>>4]; 268 GrPlot(res,j,i); 269 ptr++; 270 j+=2; 271 } else { 272 res->color=palette_map[*ptr++&15]; 273 while (cnt--) 274 GrPlot(res,j++,i); 275 } 276 } 277 } 278 ptr++; 279 } else 280 for (j=0;j<(bmp->width+7)&~7;) { 281 res->color=palette_map[*ptr&15]; 282 GrPlot(res,j+1,i); 283 res->color=palette_map[*ptr>>4]; 284 GrPlot(res,j,i); 285 ptr++; 286 j+=2; 287 } 288 if (!use_ms_paint_palette) 289 Free(palette_map); 290 } else if (bmp->bit_cnt==24) { 291 for (i=bmp->height-1;i>=0;i--) { 292 for (j=0;j<bmp->width;j++,ptr+=3) { 293 res->color=BMP24Color(ptr,dither_probability); 294 GrPlot(res,j,i); 295 } 296 ptr+=bmp->width&3; 297 } 298 if (!use_ms_paint_palette) 299 Free(palette_map); 300 } else if (bmp->bit_cnt>=32) { 301 for (i=bmp->height-1;i>=0;i--) 302 for (j=0;j<bmp->width;j++,ptr+=4) { 303 res->color=BMP24Color(ptr,dither_probability); 304 GrPlot(res,j,i); 305 } 306 if (!use_ms_paint_palette) 307 Free(palette_map); 308 } else { 309 "Format Not Supported.\n"; 310 DCDel(res); 311 res=NULL; 312 } 313 } else 314 "Invalid BMP File\n"; 315 Free(bmp); 316 } 317 return res; 318 } 319 320 #help_index "Graphics/Sprite;Graphics/Windows BMP Files;"\ 321 "DolDoc/Output;StdOut/DolDoc" 322 public U0 DocBMP(CDoc *doc=NULL,U8 *filename,Bool dither_probability=FALSE, 323 Bool use_ms_paint_palette=FALSE) 324 {//Put a BMP file into a document as a sprite. 325 CDC *dc=BMPRead(filename,dither_probability,use_ms_paint_palette); 326 U8 *elems=DC2Sprite(dc); 327 DocSprite(doc,elems); 328 Free(elems); 329 DCDel(dc); 330 } 331 332 #help_index "Graphics/Windows BMP Files;Graphics/Scrn" 333 public I64 BMPScrnCapture(U8 *filename,I64 bits=4,Bool include_zoom=TRUE) 334 {//Capture scrn as BMP file. 335 CDC *dc=DCScrnCapture(include_zoom); 336 I64 size=BMPWrite(filename,dc,bits); 337 DCDel(dc); 338 return size; 339 } 340 341 public I64 GR2BMPLst(U8 *files_find_mask,U8 *fu_flags=NULL, 342 U8 *out_print_fmt="~:/Tmp/VID%05d.BMP.Z",F64 fps=30000.0/1001) 343 {/*Cvt movie from GR lst to BMP lst 344 "+d" will delete GRLst files. 345 */ 346 I64 res=0,fuf_flags=0; 347 CDirEntry *tmpde,*tmpde1; 348 CDC *dc,*base=DCNew(GR_WIDTH,GR_HEIGHT); 349 U8 *st,*last_st; 350 CDate in_cdt,out_cdt=I64_MIN; 351 Bool old_silent; 352 ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),"+f+F"); 353 ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),fu_flags); 354 tmpde=tmpde1=FilesFind(files_find_mask,fuf_flags&FUG_FILES_FIND); 355 last_st=MStrPrint(out_print_fmt,0); 356 progress1_max=LinkedLstCnt(tmpde); 357 while (tmpde) { 358 dc=GRRead(tmpde->full_name); 359 GrBlot(base,dc->x0,dc->y0,dc); 360 in_cdt=Str2I64(tmpde->name,16); 361 if (out_cdt==I64_MIN) 362 out_cdt=in_cdt; 363 while (out_cdt<=in_cdt) { 364 st=MStrPrint(out_print_fmt,res++); 365 BMPWrite(st,base); 366 Free(st); 367 out_cdt+=CDATE_FREQ/fps; 368 } 369 if (fuf_flags&FUF_DEL) { 370 old_silent=Silent; 371 Del(tmpde->full_name); 372 Silent(old_silent); 373 } 374 DCDel(dc); 375 progress1++; 376 tmpde=tmpde->next; 377 } 378 progress1=progress1_max=0; 379 DirTreeDel(tmpde1); 380 Free(last_st); 381 DCDel(base); 382 return res; 383 }