FreeBASIC  0.91.0
gfx_bload.c
Go to the documentation of this file.
1 /* BLOAD support. */
2 
3 #include "fb_gfx.h"
4 #ifdef HOST_WIN32
5  #include <windows.h>
6 #endif
7 
8 #ifndef BI_RGB
9 #define BI_RGB 0
10 #define BI_RLE8 1
11 #define BI_RLE4 2
12 #define BI_BITFIELDS 3
13 #endif
14 
15 static inline int fread_16_le(uint16_t *buf, FILE *f)
16 {
17  int rc;
18  rc = fread(buf, sizeof(*buf), 1, f);
19  /* TODO: byteswap on BE */
20  return rc;
21 }
22 
23 static inline int fread_32_le(uint32_t *buf, FILE *f)
24 {
25  int rc;
26  rc = fread(buf, sizeof(*buf), 1, f);
27  /* TODO: byteswap on BE */
28  return rc;
29 }
30 
31 typedef void (*FBGFX_BLOAD_IMAGE_CONVERT)(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits);
32 
33 static void convert_8to8(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
34 {
35  fb_image_convert_8to8(src, dest, w);
36 }
37 
38 static void convert_8to16(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
39 {
40  fb_image_convert_8to16(src, dest, w);
41 }
42 
43 static void convert_8to32(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
44 {
45  fb_image_convert_8to32(src, dest, w);
46 }
47 
48 #define CONVERT_DEPTH(c, from, to) \
49  ((from) <= (to) ? \
50  ((c) << (to - from)) | (c >> (from - (to - from))) \
51  : (c) >> (from - to))
52 
53 static void convert_bf_16to16(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
54 {
55  uint32_t r, g, b;
56  uint16_t *s = (uint16_t *)src;
57  uint16_t *d = (uint16_t *)dest;
58  for (; w; w--) {
59  r = (*s >> shifts[0]) & masks[0];
60  g = (*s >> shifts[1]) & masks[1];
61  b = (*s >> shifts[2]) & masks[2];
62  r = CONVERT_DEPTH(r, bits[0], 5);
63  g = CONVERT_DEPTH(g, bits[1], 6);
64  b = CONVERT_DEPTH(b, bits[2], 5);
65  *d = (r << 11) | (g << 5) | b;
66  s++;
67  d++;
68  }
69 }
70 
71 static void convert_bf_16to32(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
72 {
73  uint32_t r, g, b;
74  uint16_t *s = (uint16_t *)src;
75  uint32_t *d = (uint32_t *)dest;
76  for (; w; w--) {
77  r = (*s >> shifts[0]) & masks[0];
78  g = (*s >> shifts[1]) & masks[1];
79  b = (*s >> shifts[2]) & masks[2];
80  r = CONVERT_DEPTH(r, bits[0], 8);
81  g = CONVERT_DEPTH(g, bits[1], 8);
82  b = CONVERT_DEPTH(b, bits[2], 8);
83  *d = (255 << 24) | (r << 16) | (g << 8) | b;
84  s++;
85  d++;
86  }
87 }
88 
89 static void convert_bf_24to16(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
90 {
91  uint32_t r, g, b;
92 
93  uint32_t c;
94  uint16_t *d = (uint16_t *)dest;
95  for (; w; w--) {
96  c = src[0] | (src[1] << 8) | (src[2] << 16);
97  r = (c >> shifts[0]) & masks[0];
98  g = (c >> shifts[1]) & masks[1];
99  b = (c >> shifts[2]) & masks[2];
100  r = CONVERT_DEPTH(r, bits[0], 5);
101  g = CONVERT_DEPTH(g, bits[1], 6);
102  b = CONVERT_DEPTH(b, bits[2], 5);
103  *d++ = (r << 11) | (g << 5) | b;
104  src += 3;
105  }
106 }
107 
108 static void convert_bf_24to32(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
109 {
110  uint32_t r, g, b;
111 
112  uint32_t c;
113  uint32_t *d = (uint32_t *)dest;
114  for (; w; w--) {
115  c = src[0] | (src[1] << 8) | (src[2] << 16);
116  r = (c >> shifts[0]) & masks[0];
117  g = (c >> shifts[1]) & masks[1];
118  b = (c >> shifts[2]) & masks[2];
119  r = CONVERT_DEPTH(r, bits[0], 8);
120  g = CONVERT_DEPTH(g, bits[1], 8);
121  b = CONVERT_DEPTH(b, bits[2], 8);
122  *d++ = (255 << 24) | (r << 16) | (g << 8) | b;
123  src += 3;
124  }
125 }
126 
127 static void convert_bf_32to16(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
128 {
129  uint32_t r, g, b;
130  uint32_t *s = (uint32_t *)src;
131  uint16_t *d = (uint16_t *)dest;
132  for (; w; w--) {
133  r = (*s >> shifts[0]) & masks[0];
134  g = (*s >> shifts[1]) & masks[1];
135  b = (*s >> shifts[2]) & masks[2];
136  r = CONVERT_DEPTH(r, bits[0], 5);
137  g = CONVERT_DEPTH(g, bits[1], 6);
138  b = CONVERT_DEPTH(b, bits[2], 5);
139  *d++ = (r << 11) | (g << 5) | b;
140  s++;
141  }
142 }
143 
144 static void convert_bf_32to32(const unsigned char *src, unsigned char *dest, int w, const uint32_t *masks, const int *shifts, const int *bits)
145 {
146  uint32_t r, g, b, a;
147  uint32_t *s = (uint32_t *)src;
148  uint32_t *d = (uint32_t *)dest;
149  for (; w; w--) {
150  r = (*s >> shifts[0]) & masks[0];
151  g = (*s >> shifts[1]) & masks[1];
152  b = (*s >> shifts[2]) & masks[2];
153  a = (*s >> shifts[3]) & masks[3];
154  r = CONVERT_DEPTH(r, bits[0], 8);
155  g = CONVERT_DEPTH(g, bits[1], 8);
156  b = CONVERT_DEPTH(b, bits[2], 8);
157  if (masks[3]) a = CONVERT_DEPTH(a, bits[3], 8);
158  *d++ = (a << 24) | (r << 16) | (g << 8) | b;
159  s++;
160  }
161 }
162 
163 static int load_bmp(FB_GFXCTX *ctx, FILE *f, void *dest, void *pal, int usenewheader)
164 {
165  uint16_t bfType;
166  uint32_t bfSize;
167  uint16_t bfReserved1;
168  uint16_t bfReserved2;
169  uint32_t bfOffBits;
170  uint32_t biSize;
171  int32_t biWidth;
172  uint16_t bcWidth;
173  int32_t biHeight;
174  uint16_t bcHeight;
175  uint16_t biPlanes;
176  uint16_t biBitCount;
177  uint32_t biCompression = BI_RGB;
178  uint32_t biSizeImage = 0;
179  int flipped = FALSE;
180 
181  PUT_HEADER *put_header = NULL;
182  unsigned char *buffer;
183  int result = fb_ErrorSetNum( FB_RTERROR_OK );
184  int i, j, width, height, bpp, color, expand, size, padding, palette[256], palette_entries;
185  int shifts[4] = {0, 0, 0, 0};
186  uint32_t masks[4];
187  int bits[4] = {0, 0, 0, 0};
188  void *target_pal = pal;
189  uint32_t rgba[4];
191  rgba[3] = 0;
192 
193  if (!__fb_gfx)
195 
196  if ((!fread_16_le(&bfType, f)) ||
197  (!fread_32_le(&bfSize, f)) ||
198  (!fread_16_le(&bfReserved1, f)) ||
199  (!fread_16_le(&bfReserved2, f)) ||
200  (!fread_32_le(&bfOffBits, f)) ||
201  (!fread_32_le(&biSize, f)))
202  return FB_RTERROR_FILEIO;
203 
204  if (biSize == 12) {
205  /* OS/2 V1 (BITMAPCOREHEADER) */
206  if ((!fread_16_le(&bcWidth, f)) ||
207  (!fread_16_le(&bcHeight, f)) ||
208  (!fread_16_le(&biPlanes, f)) ||
209  (!fread_16_le(&biBitCount, f)))
210  return FB_RTERROR_FILEIO;
211  biWidth = bcWidth;
212  biHeight = bcHeight;
213  } else if (biSize >= 16) {
214  /* Windows V3 (BITMAPINFOHEADER) or OS/2 V2 (BITMAPINFOHEADER2) */
215  if ((!fread_32_le((uint32_t *)&biWidth, f)) ||
216  (!fread_32_le((uint32_t *)&biHeight, f)) ||
217  (!fread_16_le(&biPlanes, f)) ||
218  (!fread_16_le(&biBitCount, f)))
219  return FB_RTERROR_FILEIO;
220 
221  if (biSize >= 20) {
222  if (!fread_32_le(&biCompression, f)) {
223  return FB_RTERROR_FILEIO;
224  }
225  if (biSize >= 24) {
226  if (!fread_32_le(&biSizeImage, f)) {
227  return FB_RTERROR_FILEIO;
228  }
229 
230  if (biSize >= 108) {
231  /* Windows V4 (BITMAPV4HEADER) or later */
232  if ((fseek(f, 4*4, SEEK_CUR)) ||
233  (!fread_32_le(&rgba[0], f)) ||
234  (!fread_32_le(&rgba[1], f)) ||
235  (!fread_32_le(&rgba[2], f)) ||
236  (!fread_32_le(&rgba[3], f))) {
237  return FB_RTERROR_FILEIO;
238  }
239  }
240  }
241  }
242  } else {
243  /* unsupported header type */
244  return FB_RTERROR_FILEIO;
245  }
246 
247  if ((bfType != 19778) || (biPlanes > 1) || (biWidth <= 0) || (biHeight == 0) || (biCompression > BI_BITFIELDS))
248  return FB_RTERROR_FILEIO;
249 
250  if (biHeight < 0) {
251  if ((biCompression != BI_RGB) && (biCompression != BI_BITFIELDS))
252  return FB_RTERROR_FILEIO;
253  flipped = TRUE;
254  biHeight = -biHeight;
255  }
256 
257  if (bfOffBits == 0) {
258  bfOffBits = biSize + 14;
259  if (biBitCount <= 8) {
260  bfOffBits += (1 << biBitCount);
261  }
262  }
263 
264  fseek(f, biSize + 14, SEEK_SET);
265 
266  if (biBitCount <= 8) {
267  /* OS/2 palette entries are 3 bytes; others are 4 bytes */
268  int pal_entry_size = (biSize == 12 ? 3 : 4);
269  palette_entries = 1 << biBitCount;
270  for (i = 0; i < palette_entries; i++) {
271  palette[i] = (fgetc(f) << 16) | (fgetc(f) << 8) | fgetc(f);
272  if (pal)
273  palette[i] = (palette[i] >> 2) & 0x3F3F3F;
274  if (pal_entry_size == 4) {
275  fgetc(f);
276  }
277  }
278  }
279  else
280  palette_entries = 0;
281  if (!pal)
282  target_pal = (void *)__fb_gfx->device_palette;
283 
284  if (dest) {
285  put_header = (PUT_HEADER *)dest;
286  /* do not overwrite pre-allocated image buffer header */
287  if (put_header->type == PUT_HEADER_NEW) {
288  width = MIN(put_header->width, biWidth);
289  height = MIN(put_header->height, biHeight);
290  bpp = put_header->bpp;
291  } else {
292  bpp = put_header->old.bpp;
293  if (bpp == 1 || bpp == 2 || bpp == 4) {
294  width = MIN(put_header->old.width, biWidth);
295  height = MIN(put_header->old.height, biHeight);
296  }
297  else {
298  if (usenewheader) {
299  put_header->type = PUT_HEADER_NEW;
300  put_header->bpp = ctx->target_bpp;
301  put_header->width = biWidth;
302  put_header->height = biHeight;
303  put_header->pitch = ((put_header->width * put_header->bpp) + 0xF) & ~0xF;
304 
305  width = MIN(put_header->width, biWidth);
306  height = MIN(put_header->height, biHeight);
307  bpp = put_header->bpp;
308  }
309  else {
310  put_header->old.bpp = ctx->target_bpp;
311  put_header->old.width = biWidth;
312  put_header->old.height = biHeight;
313  put_header->pitch = ((put_header->width * put_header->bpp) + 0xF) & ~0xF;
314 
315  width = MIN(put_header->old.width, biWidth);
316  height = MIN(put_header->old.height, biHeight);
317  bpp = put_header->old.bpp;
318  }
319  }
320  }
321  }
322  else {
323  width = MIN(__fb_gfx->w, biWidth);
324  height = MIN(__fb_gfx->h, biHeight);
325  bpp = __fb_gfx->bpp;
326  }
327  fb_hPrepareTarget(ctx, dest);
329 
330  expand = (biBitCount < 8) ? biBitCount : 0;
331  if (biCompression == BI_BITFIELDS) {
332  if (biSize < 108) {
333  if (!fread(rgba, 12, 1, f))
334  return FB_RTERROR_FILEIO;
335  }
336  } else if (biBitCount <= 16) {
337  rgba[0] = 0x7C00;
338  rgba[1] = 0x3E0;
339  rgba[2] = 0x1F;
340  } else {
341  rgba[0] = 0xFF0000;
342  rgba[1] = 0xFF00;
343  rgba[2] = 0xFF;
344  rgba[3] = 0xFF000000;
345  }
346  if (biBitCount <= 8) {
347  switch (bpp) {
348  case 1: convert = convert_8to8; break;
349  case 2: convert = convert_8to16; break;
350  case 3:
351  case 4: convert = convert_8to32; break;
352  }
353  }
354  else if (biBitCount <= 16) {
355  switch (bpp) {
356  case 1: return FB_RTERROR_ILLEGALFUNCTIONCALL;
357  case 2: convert = convert_bf_16to16; break;
358  case 3:
359  case 4: convert = convert_bf_16to32; break;
360  }
361  }
362  else if (biBitCount <= 24) {
363  switch (bpp) {
364  case 1: return FB_RTERROR_ILLEGALFUNCTIONCALL;
365  case 2: convert = convert_bf_24to16; break;
366  case 3:
367  case 4: convert = convert_bf_24to32; break;
368  }
369  }
370  else if (biBitCount <= 32) {
371  switch (bpp) {
372  case 1: return FB_RTERROR_ILLEGALFUNCTIONCALL;
373  case 2: convert = convert_bf_32to16; break;
374  case 3:
375  case 4: convert = convert_bf_32to32; break;
376  }
377  }
378  else
379  return FB_RTERROR_FILEIO;
380 
381  /* calculate shifts and masks from bitfields to convert
382  * source pixels into fbgfx format */
383  for (i = 0; i < 4; i++) {
384  masks[i] = rgba[i];
385  if (masks[i]) {
386  while ((~masks[i]) & 1) {
387  shifts[i]++;
388  masks[i] >>= 1;
389  }
390  while (masks[i]) {
391  bits[i]++;
392  masks[i] >>= 1;
393  }
394  masks[i] = rgba[i] >> shifts[i];
395  }
396  }
397 
398  DRIVER_LOCK();
399  fb_hMemCpy(target_pal, palette, palette_entries * sizeof(int));
400  if (!pal)
402  size = ((biWidth * BYTES_PER_PIXEL(biBitCount)) + 3) & ~0x3;
403  buffer = (unsigned char *)malloc(size + 1);
404  switch (expand) {
405  case 1: padding = 4 - (((biWidth + 7) >> 3) & 0x3); break;
406  case 4: padding = 4 - (((biWidth + 1) >> 1) & 0x3); break;
407  default: padding = 4 - ((biWidth * BYTES_PER_PIXEL(biBitCount)) & 0x3); break;
408  }
409  padding &= 0x3;
410  fseek(f, bfOffBits, SEEK_SET);
411  for (i = flipped ? 0 : (biHeight - 1);
412  flipped ? (i < biHeight) : (i >= 0);
413  flipped ? i++ : i--) {
414 
415  if (expand) {
416  color = 0;
417  for (j = 0; j < biWidth; j++) {
418  if (j % (8 / expand) == 0) {
419  if ((color = fgetc(f)) < 0) {
420  result = FB_RTERROR_FILEIO;
421  goto exit_error;
422  }
423  }
424  buffer[j] = color >> (8 - expand);
425  color = (color << expand) & 0xFF;
426  }
427  for (j = 0; j < padding; j++)
428  fgetc(f);
429  }
430  else if (!fread(buffer, size, 1, f)) {
431  result = FB_RTERROR_FILEIO;
432  break;
433  }
434  if (i < height)
435  convert(buffer, ctx->line[i], width, masks, shifts, bits);
436  }
437 
438 exit_error:
439  SET_DIRTY(ctx, 0, __fb_gfx->h);
440  DRIVER_UNLOCK();
441 
442  free(buffer);
443 
444  return result;
445 }
446 
447 static int gfx_bload(FBSTRING *filename, void *dest, void *pal, int usenewheader)
448 {
449  FILE *f;
451  unsigned char id;
452  unsigned int color, *palette = pal, size = 0;
453  char buffer[MAX_PATH];
454  int i, result = fb_ErrorSetNum( FB_RTERROR_OK );
455 
456  if ((!dest) && (!__fb_gfx))
458 
459  snprintf(buffer, MAX_PATH-1, "%s", filename->data);
460  buffer[MAX_PATH-1] = '\0';
461  fb_hConvertPath(buffer);
462 
463  f = fopen(buffer, "rb");
464 
465  if (!f) {
466  fb_hStrDelTemp(filename);
468  }
469 
470  fb_hPrepareTarget(context, NULL);
472 
473  id = fgetc(f);
474  switch (id) {
475 
476  case 0xFD:
477  /* QB BSAVEd block */
478  fgetc(f); fgetc(f); fgetc(f); fgetc(f);
479  size = fgetc(f) | (fgetc(f) << 8);
480  break;
481 
482  case 0xFE:
483  /* FB BSAVEd block */
484  size = fgetc(f) | (fgetc(f) << 8) | (fgetc(f) << 16) | (fgetc(f) << 24);
485  break;
486 
487  case 'B':
488  /* Can be a BMP */
489  rewind(f);
490  result = load_bmp(context, f, dest, pal, usenewheader);
491  fclose(f);
492  fb_hStrDelTemp(filename);
493  return result;
494 
495  default:
496  result = FB_RTERROR_FILEIO;
497  break;
498  }
499 
500  if (result == FB_RTERROR_OK) {
501  if (!dest) {
502  DRIVER_LOCK();
503  size = MIN(size, __fb_gfx->pitch * __fb_gfx->h);
504  if ((!fread(context->line[0], size, 1, f)) && (!feof(f)))
505  result = FB_RTERROR_FILEIO;
506  SET_DIRTY(context, 0, __fb_gfx->h);
507  if (!feof(f)) {
508  if (!pal)
509  palette = __fb_gfx->device_palette;
510  for (i = 0; i < (1 << __fb_gfx->depth); i++) {
511  color = fgetc(f) | (fgetc(f) << 8) | (fgetc(f) << 16);
512  if (!pal)
513  color = (color << 2) & 0xFCFCFC;
514  palette[i] = color;
515  }
516  if (!pal)
518  }
519  DRIVER_UNLOCK();
520  }
521  else {
522  if ((!fread(dest, size, 1, f)) && (!feof(f)))
523  result = FB_RTERROR_FILEIO;
524  }
525  }
526  fclose(f);
527 
528  fb_hStrDelTemp(filename);
529 
530  return fb_ErrorSetNum( result );
531 }
532 
533 FBCALL int fb_GfxBload(FBSTRING *filename, void *dest, void *pal)
534 {
535  return gfx_bload( filename, dest, pal, TRUE );
536 }
537 
538 FBCALL int fb_GfxBloadQB(FBSTRING *filename, void *dest, void *pal)
539 {
540  return gfx_bload( filename, dest, pal, FALSE );
541 }