FreeBASIC  0.91.0
gfx_bsave.c
Go to the documentation of this file.
1 /* BSAVE support. */
2 
3 #include "fb_gfx.h"
4 #ifdef HOST_WIN32
5  #include <windows.h>
6 #endif
7 
8 typedef struct BMP_HEADER
9 {
10  unsigned short bfType;
11  unsigned int bfSize;
12  unsigned short bfReserved1;
13  unsigned short bfReserved2;
14  unsigned int bfOffBits;
15  unsigned int biSize;
16  unsigned int biWidth;
17  unsigned int biHeight;
18  unsigned short biPlanes;
19  unsigned short biBitCount;
20  unsigned int biCompression;
21  unsigned int biSizeImage;
22  unsigned int biXPelsPerMeter;
23  unsigned int biYPelsPerMeter;
24  unsigned int biClrUsed;
25  unsigned int biClrImportant;
27 
28 
29 /*:::::*/
30 static int save_bmp(FB_GFXCTX *ctx, FILE *f, void *src, void *pal, int outbpp)
31 {
32  BMP_HEADER header;
33  PUT_HEADER *put_header;
34  int w, h, i, bfSize, biSizeImage, bfOffBits, biClrUsed, inbpp, inpitch, outpitch, color;
35  unsigned char *s, *buffer, *p;
36  unsigned int *palette = (unsigned int *)pal, *paltmp = 0;
37 
38  if (src) {
39  put_header = (PUT_HEADER *)src;
40  if (put_header->type == PUT_HEADER_NEW) {
41  w = put_header->width;
42  h = put_header->height;
43  s = (unsigned char *)src + sizeof(PUT_HEADER);
44  inbpp = put_header->bpp;
45  inpitch = put_header->pitch;
46  }
47  else {
48  w = put_header->old.width;
49  h = put_header->old.height;
50  s = (unsigned char *)src + 4;
51  inbpp = (put_header->old.bpp ? put_header->old.bpp : __fb_gfx->bpp);
52  inpitch = w * inbpp;
53  }
54  }
55  else {
56  w = __fb_gfx->w;
57  h = __fb_gfx->h;
58  s = ctx->line[0];
59  inbpp = __fb_gfx->bpp;
60  inpitch = __fb_gfx->pitch;
61  }
62 
63  if (w <= 0 || h <= 0 || s == 0 || inpitch < w * inbpp) {
64  /* Something wrong with the image header */
66  }
67 
68  switch (inbpp) {
69  case 1: /* 8-bit or 24-bit output (default to 8) */
70  if (outbpp > 8) {
71  outbpp = 24;
72  }
73  else {
74  outbpp = 8;
75  }
76  break;
77  case 2: /* 24-bit output only */
78  outbpp = 24;
79  break;
80  case 4: /* 24-bit or 32-bit output only (default to 32) */
81  if (outbpp != 24) {
82  outbpp = 32;
83  }
84  break;
85  default:
87  }
88 
89  /* Change bits/pixel to bytes/pixel */
90  outbpp >>= 3;
91 
92  switch (outbpp) {
93  case 1:
94  outpitch = ( (w + 3) & ~3 );
95  biSizeImage = outpitch * h;
96  bfOffBits = 54 + 256*4;
97  bfSize = bfOffBits + biSizeImage;
98  biClrUsed = 256;
99  break;
100  case 3:
101  outpitch = ( ((w * 3) + 3) & ~3 );
102  biSizeImage = outpitch * h;
103  bfOffBits = 54;
104  bfSize = bfOffBits + biSizeImage;
105  biClrUsed = 0;
106  break;
107  case 4:
108  default:
109  DBG_ASSERT(outbpp == 4);
110  outpitch = w * 4;
111  biSizeImage = outpitch * h;
112  bfOffBits = 54;
113  bfSize = bfOffBits + biSizeImage;
114  biClrUsed = 0;
115  break;
116  }
117 
118  fb_hMemSet(&header, 0, sizeof(header));
119  header.bfType = 0x4D42; /* 'B' 'M' */
120  header.bfSize = bfSize;
121  header.bfOffBits = bfOffBits;
122  header.biSize = 40;
123  header.biWidth = w;
124  header.biHeight = h;
125  header.biPlanes = 1;
126  header.biBitCount = outbpp * 8;
127  header.biSizeImage = biSizeImage;
128  header.biXPelsPerMeter = 0xB12;
129  header.biYPelsPerMeter = 0xB12;
130  header.biClrUsed = biClrUsed;
131  header.biClrImportant = biClrUsed;
132  if (!fwrite(&header, 54, 1, f))
133  return FB_RTERROR_FILEIO;
134 
135  if (inbpp == 1) {
136  if (!pal) {
137  palette = __fb_gfx->device_palette;
138  }
139  else {
140  paltmp = (unsigned int *)calloc(sizeof(unsigned int), 256);
141  if (paltmp == 0) {
142  return FB_RTERROR_OUTOFMEM;
143  }
144  for (i = 0; i < 256; i++) {
145  paltmp[i] = (palette[i] & 0x3f0000) >> (16 - 2)
146  | (palette[i] & 0x003f00) << 2
147  | (palette[i] & 0x00003f) << (16 + 2);
148  }
149  palette = paltmp;
150  }
151 
152  if (outbpp == 1) {
153  for (i = 0; i < 256; i++) {
154  fputc(((palette[i] >> 16) & 0xFF), f);
155  fputc(((palette[i] >> 8) & 0xFF), f);
156  fputc((palette[i] & 0xFF), f);
157  fputc(0, f);
158  }
159  }
160  }
161 
162  buffer = (unsigned char *)calloc(1, outpitch + 15);
163  if (buffer == 0) {
164  if (paltmp) free(paltmp);
165  return FB_RTERROR_OUTOFMEM;
166  }
167 
168  s += (h - 1) * inpitch;
169  for (; h; h--) {
170  p = buffer;
171  switch (inbpp) {
172  case 1:
173  if (outbpp == 1) {
174  fb_hMemCpy(p, s, inpitch);
175  }
176  else {
177  DBG_ASSERT(outbpp == 3);
178  for (i = 0; i < w; i++) {
179  color = palette[((unsigned char *)s)[i]];
180  *p++ = (color & 0xFF0000) >> 16;
181  *p++ = (color & 0xFF00) >> 8;
182  *p++ = (color & 0xFF);
183  }
184  }
185  break;
186  case 2:
187  DBG_ASSERT(outbpp == 3);
188  for (i = 0; i < w; i++) {
189  color = ((unsigned short *)s)[i];
190  *p++ = ((color & 0x001F) << 3) | ((color & 0x001F) >> 2);
191  *p++ = ((color & 0x07E0) >> 3) | ((color & 0x07E0) >> 9);
192  *p++ = ((color & 0xF800) >> 8) | ((color & 0xF800) >> 13);
193  }
194  break;
195  case 4:
196  default:
197  DBG_ASSERT(inbpp == 4);
198  DBG_ASSERT(outbpp == 3 || outbpp == 4);
199  for (i = 0; i < w; i++) {
200  *(unsigned int *)p = ((unsigned int *)s)[i];
201  p += outbpp;
202  }
203  break;
204  }
205  if (!fwrite(buffer, outpitch, 1, f))
206  return FB_RTERROR_FILEIO;
207  s -= inpitch;
208  }
209 
210  free(buffer);
211  if (paltmp) free(paltmp);
212 
213  return fb_ErrorSetNum( FB_RTERROR_OK );
214 }
215 
216 
217 /*:::::*/
218 FBCALL int fb_GfxBsaveEx(FBSTRING *filename, void *src, unsigned int size, void *pal, int bitsperpixel)
219 {
220  FILE *f;
222  int i, result = fb_ErrorSetNum( FB_RTERROR_OK );
223  unsigned int color, *palette = (unsigned int *)pal;
224  char buffer[MAX_PATH], *p;
225 
226  snprintf(buffer, MAX_PATH-1, "%s", filename->data);
227  buffer[MAX_PATH-1] = '\0';
228  fb_hConvertPath(buffer);
229 
230  f = fopen(buffer, "wb");
231  if (!f) {
232  fb_hStrDelTemp(filename);
234  }
235 
236  fb_hPrepareTarget(context, NULL);
238 
239  p = strrchr(filename->data, '.');
240  if ((p) && (!strcasecmp(p + 1, "bmp")))
241  result = save_bmp(context, f, src, pal, bitsperpixel);
242  else {
243  if ((size == 0) && src) {
244  fclose(f);
245  fb_hStrDelTemp(filename);
247  }
248 
249  fputc(0xFE, f);
250  fputc(size & 0xFF, f);
251  fputc((size >> 8) & 0xFF, f);
252  fputc((size >> 16) & 0xFF, f);
253  fputc(size >> 24, f);
254 
255  if (!src) {
256  DRIVER_LOCK();
257  size = MIN(size, __fb_gfx->pitch * __fb_gfx->h);
258  if (!fwrite(context->line[0], size, 1, f))
259  result = FB_RTERROR_FILEIO;
260  DRIVER_UNLOCK();
261  if (__fb_gfx->depth <= 8) {
262  for (i = 0; i < (1 << __fb_gfx->depth); i++) {
263  if (pal)
264  color = palette[i];
265  else
266  color = (__fb_gfx->device_palette[i] >> 2) & 0x3F3F3F;
267  fputc(color & 0xFF, f);
268  fputc((color >> 8) & 0xFF, f);
269  fputc((color >> 16) & 0xFF, f);
270  }
271  }
272  }
273  else if (!fwrite(src, size, 1, f))
274  result = FB_RTERROR_FILEIO;
275  }
276 
277  fclose(f);
278 
279  fb_hStrDelTemp(filename);
280 
281  return fb_ErrorSetNum( result );
282 }
283 
284 
285 /*:::::*/
286 FBCALL int fb_GfxBsave(FBSTRING *filename, void *src, unsigned int size, void *pal)
287 {
288  return fb_GfxBsaveEx(filename, src, size, pal, 0);
289 }