|
| 1 | +#help_index "Graphics/Windows BMP Files" |
| 2 | + |
| 3 | +#define BMP_COLORS_NUM 16 |
| 4 | + |
| 5 | +class CFileBMP |
| 6 | +{ |
| 7 | + U16 type; |
| 8 | + U32 file_size; |
| 9 | + U32 reserved; |
| 10 | + U32 data_offset; |
| 11 | + |
| 12 | + U32 header_size; |
| 13 | + U32 width; |
| 14 | + U32 height; |
| 15 | + U16 planes; |
| 16 | + U16 bit_cnt; |
| 17 | + U32 compression; |
| 18 | + U32 image_size; |
| 19 | + U32 x_pixs_per_meter; |
| 20 | + U32 y_pixs_per_meter; |
| 21 | + U32 colors_used; |
| 22 | + U32 important_colors; |
| 23 | + |
| 24 | + U0 end; |
| 25 | + |
| 26 | + CBGR24 palette[BMP_COLORS_NUM]; |
| 27 | +}; |
| 28 | + |
| 29 | +public CFileBMP *BMP4To(CDC *dc) |
| 30 | +{//To Windows 4-bit BMP. |
| 31 | + U8 *src,*ptr; |
| 32 | + CBGR48 palette[COLORS_NUM]; |
| 33 | + I64 i,x,y,w=dc->width>>1, |
| 34 | + size=sizeof(CFileBMP)+dc->width*dc->height>>1; |
| 35 | + CFileBMP *bmp =CAlloc(size); |
| 36 | + bmp->type ='BM'; |
| 37 | + bmp->planes =1; |
| 38 | + bmp->file_size=size; |
| 39 | + bmp->data_offset=sizeof(CFileBMP); |
| 40 | + bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size); |
| 41 | + bmp->width =dc->width; |
| 42 | + bmp->height =dc->height; |
| 43 | + bmp->bit_cnt =4; |
| 44 | + bmp->image_size=dc->width*dc->height>>1; |
| 45 | + GrPaletteGet(palette); |
| 46 | +#assert COLORS_NUM==BMP_COLORS_NUM |
| 47 | + for (i=0;i<BMP_COLORS_NUM;i++) { |
| 48 | + bmp->palette[i].b=palette[i].b>>8; |
| 49 | + bmp->palette[i].g=palette[i].g>>8; |
| 50 | + bmp->palette[i].r=palette[i].r>>8; |
| 51 | + bmp->palette[i].pad=0; |
| 52 | + } |
| 53 | + ptr=bmp(U8 *)+bmp->data_offset; |
| 54 | + for (y=dc->height-1;y>=0;y--) { |
| 55 | + src=y*dc->width_internal+dc->body; |
| 56 | + for (x=0;x<w;x++) { |
| 57 | + *ptr|=(*src++&15)<<4; |
| 58 | + *ptr|=*src++&15; |
| 59 | + ptr++; |
| 60 | + } |
| 61 | + } |
| 62 | + return bmp; |
| 63 | +} |
| 64 | + |
| 65 | +public CFileBMP *BMPRLE4To(CDC *dc) |
| 66 | +{//To Windows RLE4 bit BMP. |
| 67 | + U8 *src,*ptr,*start; |
| 68 | + I64 i,x,y,w=dc->width,cnt,pattern; |
| 69 | + CBGR48 palette[COLORS_NUM]; |
| 70 | + CFileBMP *bmp =CAlloc(sizeof(CFileBMP)+2*(dc->width+1)*dc->height); |
| 71 | + bmp->type ='BM'; |
| 72 | + bmp->planes =1; |
| 73 | + bmp->data_offset=sizeof(CFileBMP); |
| 74 | + bmp->header_size=offset(CFileBMP.end)-offset(CFileBMP.header_size); |
| 75 | + bmp->width =dc->width; |
| 76 | + bmp->height =dc->height; |
| 77 | + bmp->bit_cnt =4; |
| 78 | + bmp->compression=2; //RLE4 |
| 79 | + GrPaletteGet(palette); |
| 80 | +#assert COLORS_NUM==BMP_COLORS_NUM |
| 81 | + for (i=0;i<BMP_COLORS_NUM;i++) { |
| 82 | + bmp->palette[i].b=palette[i].b>>8; |
| 83 | + bmp->palette[i].g=palette[i].g>>8; |
| 84 | + bmp->palette[i].r=palette[i].r>>8; |
| 85 | + bmp->palette[i].pad=0; |
| 86 | + } |
| 87 | + start=ptr=bmp(U8 *)+bmp->data_offset; |
| 88 | + for (y=dc->height-1;y>=0;y--) { |
| 89 | + src=y*dc->width_internal+dc->body; |
| 90 | + x=0; |
| 91 | + while (x<w) { |
| 92 | + pattern=(src[0]&15)<<4+src[1]&15; |
| 93 | + if (x+1<w && src[0]&15==src[1]&15) { |
| 94 | + src+=2; |
| 95 | + cnt=2; |
| 96 | + x+=2; |
| 97 | + while (x<w && cnt<U8_MAX) { |
| 98 | + if (*src&15==pattern&15) { |
| 99 | + 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 | + if (bmp->file_size>sizeof(CFileBMP)+dc->width*dc->height>>1) { |
| 168 | + Free(bmp); |
| 169 | + bmp=BMP4To(dc); |
| 170 | + } |
| 171 | + } |
| 172 | + } else if (bits==24) |
| 173 | + bmp=BMP24To(dc); |
| 174 | + else { |
| 175 | + "Format Not Supported.\n"; |
| 176 | + return 0; |
| 177 | + } |
| 178 | + size=bmp->file_size; |
| 179 | + FileWrite(filename,bmp,bmp->file_size); |
| 180 | + Free(bmp); |
| 181 | + return size; |
| 182 | +} |
| 183 | + |
| 184 | +U8 *BMPPaletteNew(CFileBMP *bmp) |
| 185 | +{ |
| 186 | + I64 i,j,best,score,best_score; |
| 187 | + CBGR48 palette[COLORS_NUM]; |
| 188 | + U8 *res=CAlloc(BMP_COLORS_NUM*sizeof(U8)); |
| 189 | + GrPaletteGet(palette); |
| 190 | +#assert COLORS_NUM==BMP_COLORS_NUM |
| 191 | + for (i=0;i<BMP_COLORS_NUM;i++) { |
| 192 | + best=i; |
| 193 | + best_score=I64_MAX; |
| 194 | + for (j=0;j<BMP_COLORS_NUM;j++) { |
| 195 | + score=SqrI64(bmp->palette[i].r-palette[j].r>>8)+ |
| 196 | + SqrI64(bmp->palette[i].g-palette[j].g>>8)+ |
| 197 | + SqrI64(bmp->palette[i].b-palette[j].b>>8); |
| 198 | + if (score<best_score) { |
| 199 | + best=j; |
| 200 | + best_score=score; |
| 201 | + } |
| 202 | + } |
| 203 | + res[i]=best; |
| 204 | + } |
| 205 | + return res; |
| 206 | +} |
| 207 | + |
| 208 | +U8 ms_paint_palette[BMP_COLORS_NUM]={0,4,2,6,1,5,3,8,7,12,10,14,9,13,11,15}; |
| 209 | + |
| 210 | +I64 BMP24Color(CBGR24 *ptr,Bool dither_probability) |
| 211 | +{ |
| 212 | + I64 res,k; |
| 213 | + if (dither_probability) { |
| 214 | + k=RandU32; |
| 215 | + if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=3*SqrI64(k.u8[0])) |
| 216 | + res=8; |
| 217 | + else |
| 218 | + res=0; |
| 219 | + if (ptr->r>=k.u8[1]) res|=RED; |
| 220 | + if (ptr->g>=k.u8[2]) res|=GREEN; |
| 221 | + if (ptr->b>=k.u8[3]) res|=BLUE; |
| 222 | + } else { |
| 223 | + if (SqrI64(ptr->r)+SqrI64(ptr->g)+SqrI64(ptr->b)>=SqrI64(0x80)) { |
| 224 | + res=8; |
| 225 | + if (ptr->r>=0x80) res|=RED; |
| 226 | + if (ptr->g>=0x80) res|=GREEN; |
| 227 | + if (ptr->b>=0x80) res|=BLUE; |
| 228 | + } else { |
| 229 | + res=0; |
| 230 | + if (ptr->r>=0x40) res|=RED; |
| 231 | + if (ptr->g>=0x40) res|=GREEN; |
| 232 | + if (ptr->b>=0x40) res|=BLUE; |
| 233 | + } |
| 234 | + } |
| 235 | + return res; |
| 236 | +} |
| 237 | + |
| 238 | +public CDC *BMPRead(U8 *filename,Bool dither_probability=FALSE, |
| 239 | + Bool use_ms_paint_palette=FALSE) |
| 240 | +{//Window's BMP Files. |
| 241 | + I64 i,j,cnt; |
| 242 | + U8 *palette_map,*ptr; |
| 243 | + Bool rle; |
| 244 | + CFileBMP *bmp; |
| 245 | + CDC *res=NULL; |
| 246 | + if (ptr=FileRead(filename)) { |
| 247 | + bmp=ptr; |
| 248 | + if (0<bmp->width<I32_MAX && 0<bmp->height<I32_MAX) { |
| 249 | + res=DCNew(bmp->width,bmp->height); |
| 250 | + ptr+=bmp->data_offset; |
| 251 | + if (bmp->compression==2) |
| 252 | + rle=TRUE; |
| 253 | + else |
| 254 | + rle=FALSE; |
| 255 | + if (use_ms_paint_palette) |
| 256 | + palette_map=ms_paint_palette; |
| 257 | + else |
| 258 | + palette_map=BMPPaletteNew(bmp); |
| 259 | + if (bmp->bit_cnt==4) { |
| 260 | + for (i=bmp->height-1;i>=0;i--) |
| 261 | + if (rle) {//We don't support full RLE4, just our own subset |
| 262 | + j=0; |
| 263 | + while (cnt=*ptr++) { |
| 264 | + if (cnt==1) { |
| 265 | + res->color=palette_map[*ptr++&15]; |
| 266 | + GrPlot(res,j++,i); |
| 267 | + } else { |
| 268 | + if (cnt==2 && *ptr>>4!=*ptr&15) { |
| 269 | + res->color=palette_map[*ptr&15]; |
| 270 | + GrPlot(res,j+1,i); |
| 271 | + res->color=palette_map[*ptr>>4]; |
| 272 | + GrPlot(res,j,i); |
| 273 | + ptr++; |
| 274 | + j+=2; |
| 275 | + } else { |
| 276 | + res->color=palette_map[*ptr++&15]; |
| 277 | + while (cnt--) |
| 278 | + GrPlot(res,j++,i); |
| 279 | + } |
| 280 | + } |
| 281 | + } |
| 282 | + ptr++; |
| 283 | + } else |
| 284 | + for (j=0;j<(bmp->width+7)&~7;) { |
| 285 | + res->color=palette_map[*ptr&15]; |
| 286 | + GrPlot(res,j+1,i); |
| 287 | + res->color=palette_map[*ptr>>4]; |
| 288 | + GrPlot(res,j,i); |
| 289 | + ptr++; |
| 290 | + j+=2; |
| 291 | + } |
| 292 | + if (!use_ms_paint_palette) |
| 293 | + Free(palette_map); |
| 294 | + } else if (bmp->bit_cnt==24) { |
| 295 | + for (i=bmp->height-1;i>=0;i--) { |
| 296 | + for (j=0;j<bmp->width;j++,ptr+=3) { |
| 297 | + res->color=BMP24Color(ptr,dither_probability); |
| 298 | + GrPlot(res,j,i); |
| 299 | + } |
| 300 | + ptr+=bmp->width&3; |
| 301 | + } |
| 302 | + if (!use_ms_paint_palette) |
| 303 | + Free(palette_map); |
| 304 | + } else if (bmp->bit_cnt>=32) { |
| 305 | + for (i=bmp->height-1;i>=0;i--) |
| 306 | + for (j=0;j<bmp->width;j++,ptr+=4) { |
| 307 | + res->color=BMP24Color(ptr,dither_probability); |
| 308 | + GrPlot(res,j,i); |
| 309 | + } |
| 310 | + if (!use_ms_paint_palette) |
| 311 | + Free(palette_map); |
| 312 | + } else { |
| 313 | + "Format Not Supported.\n"; |
| 314 | + DCDel(res); |
| 315 | + res=NULL; |
| 316 | + } |
| 317 | + } else |
| 318 | + "Invalid BMP File\n"; |
| 319 | + Free(bmp); |
| 320 | + } |
| 321 | + return res; |
| 322 | +} |
| 323 | + |
| 324 | +#help_index "Graphics/Sprite;Graphics/Windows BMP Files;"\ |
| 325 | + "DolDoc/Output;StdOut/DolDoc" |
| 326 | +public U0 DocBMP(CDoc *doc=NULL,U8 *filename,Bool dither_probability=FALSE, |
| 327 | + Bool use_ms_paint_palette=FALSE) |
| 328 | +{//Put a BMP file into a document as a sprite. |
| 329 | + CDC *dc=BMPRead(filename,dither_probability,use_ms_paint_palette); |
| 330 | + U8 *elems=DC2Sprite(dc); |
| 331 | + DocSprite(doc,elems); |
| 332 | + Free(elems); |
| 333 | + DCDel(dc); |
| 334 | +} |
| 335 | + |
| 336 | +#help_index "Graphics/Windows BMP Files;Graphics/Scrn" |
| 337 | +public I64 BMPScrnCapture(U8 *filename,I64 bits=4,Bool include_zoom=TRUE) |
| 338 | +{//Capture scrn as BMP file. |
| 339 | + CDC *dc=DCScrnCapture(include_zoom); |
| 340 | + I64 size=BMPWrite(filename,dc,bits); |
| 341 | + DCDel(dc); |
| 342 | + return size; |
| 343 | +} |
| 344 | + |
| 345 | +public I64 GR2BMPLst(U8 *files_find_mask,U8 *fu_flags=NULL, |
| 346 | + U8 *out_print_fmt="~:/Tmp/VID%05d.BMP.Z",F64 fps=30000.0/1001) |
| 347 | +{/*Cvt movie from GR lst to BMP lst |
| 348 | +"+d" will delete GRLst files. |
| 349 | +*/ |
| 350 | + I64 res=0,fuf_flags=0; |
| 351 | + CDirEntry *tmpde,*tmpde1; |
| 352 | + CDC *dc,*base=DCNew(GR_WIDTH,GR_HEIGHT); |
| 353 | + U8 *st,*last_st; |
| 354 | + CDate in_cdt,out_cdt=I64_MIN; |
| 355 | + Bool old_silent; |
| 356 | + ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),"+f+F"); |
| 357 | + ScanFlags(&fuf_flags,Define("ST_FILE_UTIL_FLAGS"),fu_flags); |
| 358 | + tmpde=tmpde1=FilesFind(files_find_mask,fuf_flags&FUG_FILES_FIND); |
| 359 | + last_st=MStrPrint(out_print_fmt,0); |
| 360 | + progress1_max=LinkedLstCnt(tmpde); |
| 361 | + while (tmpde) { |
| 362 | + dc=GRRead(tmpde->full_name); |
| 363 | + GrBlot(base,dc->x0,dc->y0,dc); |
| 364 | + in_cdt=Str2I64(tmpde->name,16); |
| 365 | + if (out_cdt==I64_MIN) |
| 366 | + out_cdt=in_cdt; |
| 367 | + while (out_cdt<=in_cdt) { |
| 368 | + st=MStrPrint(out_print_fmt,res++); |
| 369 | + BMPWrite(st,base); |
| 370 | + Free(st); |
| 371 | + out_cdt+=CDATE_FREQ/fps; |
| 372 | + } |
| 373 | + if (fuf_flags&FUF_DEL) { |
| 374 | + old_silent=Silent; |
| 375 | + Del(tmpde->full_name); |
| 376 | + Silent(old_silent); |
| 377 | + } |
| 378 | + DCDel(dc); |
| 379 | + progress1++; |
| 380 | + tmpde=tmpde->next; |
| 381 | + } |
| 382 | + progress1=progress1_max=0; |
| 383 | + DirTreeDel(tmpde1); |
| 384 | + Free(last_st); |
| 385 | + DCDel(base); |
| 386 | + return res; |
| 387 | +} |
0 commit comments