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 }