µGFX  2.9
version 2.9
gdisp_image_bmp.c
1 /*
2  * This file is subject to the terms of the GFX License. If a copy of
3  * the license was not distributed with this file, you can obtain one at:
4  *
5  * http://ugfx.io/license.html
6  */
7 
8 #include "../../gfx.h"
9 
10 #if GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP
11 
12 #include "gdisp_image_support.h"
13 
14 #if GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE * (COLOR_TYPE_BITS/8) < 40
15  #if GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_DIRECT
16  #warning "GDISP: GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE must be at least 40 bytes. It has been adjusted for you."
17  #elif GFX_COMPILER_WARNING_TYPE == GFX_COMPILER_WARNING_MACRO
18  COMPILER_WARNING("GDISP: GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE must be at least 40 bytes. It has been adjusted for you.")
19  #endif
20  #undef GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE
21  #define GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE ((40 + (COLOR_TYPE_BITS/8) - 1) / (COLOR_TYPE_BITS/8))
22 #endif
23 
24 typedef struct gdispImagePrivate_BMP {
25  gU8 bmpflags;
26  #define BMP_V2 0x01 // Version 2 (old) header format
27  #define BMP_V4 0x02 // Version 4 (alpha support) header format
28  #define BMP_PALETTE 0x04 // Uses a palette
29  #define BMP_COMP_RLE 0x08 // Uses RLE compression
30  #define BMP_COMP_MASK 0x10 // Uses mask & shift decoding
31  #define BMP_RLE_ENC 0x20 // Currently in RLE encoded run
32  #define BMP_RLE_ABS 0x40 // Currently in RLE absolute run
33  #define BMP_TOP_TO_BOTTOM 0x80 // Decodes bottom to top line
34  gU8 bitsperpixel;
35 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
36  gU16 palsize;
37  gPixel *palette;
38 #endif
39 #if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE
40  gU16 rlerun;
41  gU8 rlecode;
42 #endif
43 #if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32
44  gI8 shiftred;
45  gI8 shiftgreen;
46  gI8 shiftblue;
47  gI8 shiftalpha;
48  gU32 maskred;
49  gU32 maskgreen;
50  gU32 maskblue;
51  gU32 maskalpha;
52 #endif
53  gFileSize frame0pos;
54  gPixel *frame0cache;
56  } gdispImagePrivate_BMP;
57 
58 void gdispImageClose_BMP(gImage *img) {
59  gdispImagePrivate_BMP *priv;
60 
61  priv = (gdispImagePrivate_BMP *)img->priv;
62  if (priv) {
63 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
64  if (priv->palette)
65  gdispImageFree(img, (void *)priv->palette, priv->palsize*sizeof(gColor));
66 #endif
67  if (priv->frame0cache)
68  gdispImageFree(img, (void *)priv->frame0cache, img->width*img->height*sizeof(gPixel));
69  gdispImageFree(img, (void *)priv, sizeof(gdispImagePrivate_BMP));
70  img->priv = 0;
71  }
72 }
73 
74 gdispImageError gdispImageOpen_BMP(gImage *img) {
75  gdispImagePrivate_BMP *priv;
76  gU8 hdr[2];
77  gU16 aword;
78  gU32 adword;
79  gU32 offsetColorTable;
80 
81  /* Read the file identifier */
82  if (gfileRead(img->f, hdr, 2) != 2)
83  return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
84 
85  /* Process the BITMAPFILEHEADER structure */
86 
87  /**
88  * We only accept Windows V2+ bitmaps.
89  * - we don't support OS/2 bitmaps, icons, pointers, or Windows V1 bitmaps.
90  */
91  if (hdr[0] != 'B' || hdr[1] != 'M')
92  return GDISP_IMAGE_ERR_BADFORMAT; // It can't be us
93 
94  /* We know we are a BMP format image */
95  img->flags = 0;
96 
97  /* Allocate our private area */
98  if (!(img->priv = gdispImageAlloc(img, sizeof(gdispImagePrivate_BMP))))
99  return GDISP_IMAGE_ERR_NOMEMORY;
100 
101  /* Initialise the essential bits in the private area */
102  priv = (gdispImagePrivate_BMP *)img->priv;
103  priv->frame0cache = 0;
104  priv->bmpflags = 0;
106  priv->palette = 0;
107 #endif
108 
109  /* Skip the size field and the 2 reserved fields */
110  if (gfileRead(img->f, priv->buf, 8) != 8)
111  goto baddatacleanup;
112 
113  /* Get the offset to the bitmap data */
114  if (gfileRead(img->f, &priv->frame0pos, 4) != 4)
115  goto baddatacleanup;
116  gdispImageMakeLE32(priv->frame0pos);
117 
118  /* Process the BITMAPCOREHEADER structure */
119 
120  /* Get the offset to the colour data */
121  if (gfileRead(img->f, &offsetColorTable, 4) != 4)
122  goto baddatacleanup;
123  gdispImageMakeLE32(offsetColorTable);
124  offsetColorTable += 14; // Add the size of the BITMAPFILEHEADER
125 
126  // Detect our bitmap version
127  if (offsetColorTable == 12+14) {
128  priv->bmpflags |= BMP_V2;
129 
130  // Read the header
131  if (gfileRead(img->f, priv->buf, 12-4) != 12-4)
132  goto baddatacleanup;
133  // Get the width
134  img->width = gdispImageGetAlignedLE16(priv->buf, 0);
135  // Get the height
136  img->height = gdispImageGetAlignedLE16(priv->buf, 2);
137  if (img->height < 0) {
138  priv->bmpflags |= BMP_TOP_TO_BOTTOM;
139  img->height = -img->height;
140  }
141  // Get the planes
142  aword = gdispImageGetAlignedLE16(priv->buf, 4);
143  if (aword != 1)
144  goto unsupportedcleanup;
145  // Get the bits per pixel
146  aword = gdispImageGetAlignedLE16(priv->buf, 6);
147  switch(aword) {
148 #if GDISP_NEED_IMAGE_BMP_1
149  case 1:
150 #endif
151 #if GDISP_NEED_IMAGE_BMP_4
152  case 4:
153 #endif
154 #if GDISP_NEED_IMAGE_BMP_8
155  case 8:
156 #endif
157 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8
158  priv->bmpflags |= BMP_PALETTE;
159  priv->palsize = 1<<aword;
160  break;
161 #endif
162 #if GDISP_NEED_IMAGE_BMP_24
163  case 24:
164  break;
165 #endif
166  default:
167  goto unsupportedcleanup;
168  }
169  priv->bitsperpixel = aword;
170 
171  } else if (offsetColorTable >= 40+14) {
172  if (offsetColorTable > 40+14)
173  priv->bmpflags |= BMP_V4;
174 
175  // Read the header
176  if (gfileRead(img->f, priv->buf, 40-4) != 40-4)
177  goto baddatacleanup;
178  // Get the width
179  adword = gdispImageGetAlignedLE32(priv->buf, 0);
180  if (adword > 32768) // This also picks up negative values
181  goto unsupportedcleanup;
182  img->width = adword;
183  // Get the height
184  adword = gdispImageGetAlignedLE32(priv->buf, 4);
185  if ((gI32)adword < 0) { // Negative test
186  priv->bmpflags |= BMP_TOP_TO_BOTTOM;
187  adword = -adword;
188  }
189  if (adword > 32768)
190  goto unsupportedcleanup;
191  img->height = adword;
192  // Get the planes
193  aword = gdispImageGetAlignedLE16(priv->buf, 8);
194  if (aword != 1)
195  goto unsupportedcleanup;
196  // Get the bits per pixel
197  aword = gdispImageGetAlignedLE16(priv->buf, 10);
198  switch(aword) {
199 #if GDISP_NEED_IMAGE_BMP_1
200  case 1:
201 #endif
202 #if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE
203  case 4:
204 #endif
205 #if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
206  case 8:
207 #endif
208 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
209  priv->bmpflags |= BMP_PALETTE;
210  priv->palsize = 1<<aword;
211  break;
212 #endif
213 #if GDISP_NEED_IMAGE_BMP_16
214  case 16:
215 #endif
216 #if GDISP_NEED_IMAGE_BMP_24
217  case 24:
218 #endif
219 #if GDISP_NEED_IMAGE_BMP_32
220  case 32:
221 #endif
222 #if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_24 || GDISP_NEED_IMAGE_BMP_32
223  break;
224 #endif
225  default:
226  goto unsupportedcleanup;
227  }
228  priv->bitsperpixel = aword;
229  // Get the compression
230  adword = gdispImageGetAlignedLE32(priv->buf, 12);
231  switch(adword) {
232  case 0: // BI_RGB - uncompressed
233  break;
234 #if GDISP_NEED_IMAGE_BMP_8_RLE
235  case 1: // BI_RLE8 compression
236  if (priv->bitsperpixel != 8)
237  goto unsupportedcleanup;
238  priv->bmpflags |= BMP_COMP_RLE;
239  break;
240 #endif
241 #if GDISP_NEED_IMAGE_BMP_4_RLE
242  case 2: // BI_RLE4 compression
243  if (priv->bitsperpixel != 4)
244  goto unsupportedcleanup;
245  priv->bmpflags |= BMP_COMP_RLE;
246  break;
247 #endif
248 #if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32
249  case 3: // BI_BITFIELDS decoding
250  if (priv->bitsperpixel < 16 || priv->bitsperpixel == 24)
251  goto unsupportedcleanup;
252  priv->bmpflags |= BMP_COMP_MASK;
253  if (priv->bmpflags & BMP_V4) // V4 stored the masks in the header
254  offsetColorTable = 40+14;
255  break;
256 #endif
257  default:
258  goto unsupportedcleanup;
259  }
260  priv->bitsperpixel = aword;
261 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
262  // Get the actual colors used
263  adword = gdispImageGetAlignedLE32(priv->buf, 28);
264  if (adword && adword < priv->palsize)
265  priv->palsize = adword;
266 #endif
267  } else
268  goto baddatacleanup;
269 
270 #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
271  /* Load the palette tables */
272  if (priv->bmpflags & BMP_PALETTE) {
273  gfileSetPos(img->f, offsetColorTable);
274 
275  if (!(priv->palette = (gColor *)gdispImageAlloc(img, priv->palsize*sizeof(gColor))))
276  return GDISP_IMAGE_ERR_NOMEMORY;
277  if (priv->bmpflags & BMP_V2) {
278  for(aword = 0; aword < priv->palsize; aword++) {
279  if (gfileRead(img->f, &priv->buf, 3) != 3) goto baddatacleanup;
280  priv->palette[aword] = RGB2COLOR(((gU8 *)priv->buf)[2], ((gU8 *)priv->buf)[1], ((gU8 *)priv->buf)[0]);
281  }
282  } else {
283  for(aword = 0; aword < priv->palsize; aword++) {
284  if (gfileRead(img->f, &priv->buf, 4) != 4) goto baddatacleanup;
285  priv->palette[aword] = RGB2COLOR(((gU8 *)priv->buf)[2], ((gU8 *)priv->buf)[1], ((gU8 *)priv->buf)[0]);
286  }
287  }
288 
289  }
290 #endif
291 
292 #if GDISP_NEED_IMAGE_BMP_16 || GDISP_NEED_IMAGE_BMP_32
293  /* Load the bit masks */
294  if (priv->bmpflags & BMP_COMP_MASK) {
295  gfileSetPos(img->f, offsetColorTable);
296  if (gfileRead(img->f, &priv->maskred, 4) != 4) goto baddatacleanup;
297  gdispImageMakeLE32(priv->maskred);
298  if (gfileRead(img->f, &priv->maskgreen, 4) != 4) goto baddatacleanup;
299  gdispImageMakeLE32(priv->maskgreen);
300  if (gfileRead(img->f, &priv->maskblue, 4) != 4) goto baddatacleanup;
301  gdispImageMakeLE32(priv->maskblue);
302  if (priv->bmpflags & BMP_V4) {
303  if (gfileRead(img->f, &priv->maskalpha, 4) != 4) goto baddatacleanup;
304  gdispImageMakeLE32(priv->maskalpha);
305  } else
306  priv->maskalpha = 0;
307  } else if (priv->bitsperpixel == 16) {
308  priv->bmpflags |= BMP_COMP_MASK;
309  priv->maskred = 0x7C00;
310  priv->maskgreen = 0x03E0;
311  priv->maskblue = 0x001F;
312  priv->maskalpha = 0;
313  } else if (priv->bitsperpixel == 32) {
314  priv->bmpflags |= BMP_COMP_MASK;
315  priv->maskred = 0x00FF0000;
316  priv->maskgreen = 0x0000FF00;
317  priv->maskblue = 0x000000FF;
318  priv->maskalpha = 0;
319  }
320 
321  /* We need to adjust the masks and calculate the shift values so the result scales 0 -> 255 */
322  if (priv->bmpflags & BMP_COMP_MASK) {
323  priv->shiftred = 0;
324  priv->shiftgreen = 0;
325  priv->shiftblue = 0;
326  if (priv->maskred) {
327  if (priv->maskred < 256)
328  for(adword = priv->maskred; adword < 128; priv->shiftred--, adword <<= 1);
329  else
330  for(adword = priv->maskred; adword > 255; priv->shiftred++, adword >>= 1);
331  }
332  if (priv->maskgreen) {
333  if (priv->maskgreen < 256)
334  for(adword = priv->maskgreen; adword < 128; priv->shiftgreen--, adword <<= 1);
335  else
336  for(adword = priv->maskgreen; adword > 255; priv->shiftgreen++, adword >>= 1);
337  }
338  if (priv->maskblue) {
339  if (priv->maskblue < 256)
340  for(adword = priv->maskblue; adword < 128; priv->shiftblue--, adword <<= 1);
341  else
342  for(adword = priv->maskblue; adword > 255; priv->shiftblue++, adword >>= 1);
343  }
344  if (priv->maskalpha) {
345  if (priv->maskalpha < 256)
346  for(adword = priv->maskalpha; adword < 128; priv->shiftalpha--, adword <<= 1);
347  else
348  for(adword = priv->maskalpha; adword > 255; priv->shiftalpha++, adword >>= 1);
349  }
350  }
351 #endif
352 
353  img->type = GDISP_IMAGE_TYPE_BMP;
354  return GDISP_IMAGE_ERR_OK;
355 
356 baddatacleanup:
357  gdispImageClose_BMP(img); // Clean up the private data area
358  return GDISP_IMAGE_ERR_BADDATA; // Oops - something wrong
359 
360 unsupportedcleanup:
361  gdispImageClose_BMP(img); // Clean up the private data area
362  return GDISP_IMAGE_ERR_UNSUPPORTED; // Not supported
363 }
364 
365 static gCoord getPixels(gImage *img, gCoord x) {
366  gdispImagePrivate_BMP * priv;
367  gColor * pc;
368  gCoord len;
369 
370  priv = (gdispImagePrivate_BMP *)img->priv;
371  pc = priv->buf;
372  len = 0;
373 
374  switch(priv->bitsperpixel) {
375 #if GDISP_NEED_IMAGE_BMP_1
376  case 1:
377  {
378  gU8 b[4];
379  gU8 m;
380 
381  priv = (gdispImagePrivate_BMP *)img->priv;
382  pc = priv->buf;
383  len = 0;
384 
385  while(x < img->width && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-32) {
386  if (gfileRead(img->f, &b, 4) != 4)
387  return 0;
388 
389  for(m=0x80; m; m >>= 1, pc++)
390  pc[0] = priv->palette[(m&b[0]) ? 1 : 0];
391  for(m=0x80; m; m >>= 1, pc++)
392  pc[0] = priv->palette[(m&b[1]) ? 1 : 0];
393  for(m=0x80; m; m >>= 1, pc++)
394  pc[0] = priv->palette[(m&b[2]) ? 1 : 0];
395  for(m=0x80; m; m >>= 1, pc++)
396  pc[0] = priv->palette[(m&b[3]) ? 1 : 0];
397  len += 32;
398  x += 32;
399  }
400  }
401  return len;
402 #endif
403 
404 #if GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_4_RLE
405  case 4:
406  #if GDISP_NEED_IMAGE_BMP_4_RLE
407  #if GDISP_NEED_IMAGE_BMP_4
408  if (priv->bmpflags & BMP_COMP_RLE)
409  #endif
410  {
411  gU8 b[4];
412 
413  while(x < img->width) {
414  if (priv->bmpflags & BMP_RLE_ENC) {
415  while (priv->rlerun && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-2 && x < img->width) {
416  *pc++ = priv->palette[priv->rlecode >> 4];
417  priv->rlerun--;
418  len++;
419  x++;
420  if (priv->rlerun) {
421  *pc++ = priv->palette[priv->rlecode & 0x0F];
422  priv->rlerun--;
423  len++;
424  x++;
425  }
426  }
427  if (priv->rlerun) // Return if we have more run to do
428  return len;
429  } else if (priv->bmpflags & BMP_RLE_ABS) {
430  while (priv->rlerun && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-2 && x < img->width) {
431  if (gfileRead(img->f, &b, 1) != 1)
432  return 0;
433  *pc++ = priv->palette[b[0] >> 4];
434  priv->rlerun--;
435  len++;
436  x++;
437  if (priv->rlerun) {
438  *pc++ = priv->palette[b[0] & 0x0F];
439  priv->rlerun--;
440  len++;
441  x++;
442  }
443  }
444  if (priv->rlerun) // Return if we have more run to do
445  return len;
446  if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary
447  if (gfileRead(img->f, &b, 1) != 1)
448  return 0;
449  }
450  }
451 
452  // We have finished the current run - read a new run
453  priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS);
454 
455  // There are always at least 2 bytes in an RLE code
456  if (gfileRead(img->f, &b, 2) != 2)
457  return 0;
458 
459  if (b[0]) { // Encoded mode
460  priv->rlerun = b[0];
461  priv->rlecode = b[1];
462  priv->bmpflags |= BMP_RLE_ENC;
463  } else if (b[1] == 0) { // End of line
464  if (x < img->width) {
465  priv->rlerun = img->width - x;
466  priv->rlecode = 0; // Who knows what color this should really be
467  priv->bmpflags |= BMP_RLE_ENC;
468  }
469  } else if (b[1] == 1) { // End of file
470  return len;
471  } else if (b[1] == 2) { // Delta x, y
472  // There are always at least 2 bytes in an RLE code
473  if (gfileRead(img->f, &b, 2) != 2)
474  return 0;
475  priv->rlerun = b[0] + (gU16)b[1] * img->width;
476  priv->rlecode = 0; // Who knows what color this should really be
477  priv->bmpflags |= BMP_RLE_ENC;
478  } else { // Absolute mode
479  priv->rlerun = b[1];
480  priv->bmpflags |= BMP_RLE_ABS;
481  }
482  }
483  return len;
484  }
485  #endif
486  #if GDISP_NEED_IMAGE_BMP_4
487  {
488  gU8 b[4];
489 
490  while(x < img->width && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-8) {
491  if (gfileRead(img->f, &b, 4) != 4)
492  return 0;
493 
494  *pc++ = priv->palette[b[0] >> 4];
495  *pc++ = priv->palette[b[0] & 0x0F];
496  *pc++ = priv->palette[b[1] >> 4];
497  *pc++ = priv->palette[b[1] & 0x0F];
498  *pc++ = priv->palette[b[2] >> 4];
499  *pc++ = priv->palette[b[2] & 0x0F];
500  *pc++ = priv->palette[b[3] >> 4];
501  *pc++ = priv->palette[b[3] & 0x0F];
502  len += 8;
503  x += 8;
504  }
505  return len;
506  }
507  #endif
508 #endif
509 
510 #if GDISP_NEED_IMAGE_BMP_8 || GDISP_NEED_IMAGE_BMP_8_RLE
511  case 8:
512  #if GDISP_NEED_IMAGE_BMP_8_RLE
513  #if GDISP_NEED_IMAGE_BMP_8
514  if (priv->bmpflags & BMP_COMP_RLE)
515  #endif
516  {
517  gU8 b[4];
518 
519  while(x < img->width) {
520  if (priv->bmpflags & BMP_RLE_ENC) {
521  while (priv->rlerun && len < GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE && x < img->width) {
522  *pc++ = priv->palette[priv->rlecode];
523  priv->rlerun--;
524  len++;
525  x++;
526  }
527  if (priv->rlerun) // Return if we have more run to do
528  return len;
529  } else if (priv->bmpflags & BMP_RLE_ABS) {
530  while (priv->rlerun && len < GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE && x < img->width) {
531  if (gfileRead(img->f, &b, 1) != 1)
532  return 0;
533  *pc++ = priv->palette[b[0]];
534  priv->rlerun--;
535  len++;
536  x++;
537  }
538  if (priv->rlerun) // Return if we have more run to do
539  return len;
540  if ((gfileGetPos(img->f) - priv->frame0pos)&1) { // Make sure we are on a word boundary
541  if (gfileRead(img->f, &b, 1) != 1)
542  return 0;
543  }
544  }
545 
546  // We have finished the current run - read a new run
547  priv->bmpflags &= ~(BMP_RLE_ENC|BMP_RLE_ABS);
548 
549  // There are always at least 2 bytes in an RLE code
550  if (gfileRead(img->f, &b, 2) != 2)
551  return 0;
552 
553  if (b[0]) { // Encoded mode
554  priv->rlerun = b[0];
555  priv->rlecode = b[1];
556  priv->bmpflags |= BMP_RLE_ENC;
557  } else if (b[1] == 0) { // End of line
558  if (x < img->width) {
559  priv->rlerun = img->width - x;
560  priv->rlecode = 0; // Who knows what color this should really be
561  priv->bmpflags |= BMP_RLE_ENC;
562  }
563  } else if (b[1] == 1) { // End of file
564  return len;
565  } else if (b[1] == 2) { // Delta x, y
566  // There are always at least 2 bytes in an RLE code
567  if (gfileRead(img->f, &b, 2) != 2)
568  return (gCoord)GDISP_IMAGE_ERR_BADDATA;
569  priv->rlerun = b[0] + (gU16)b[1] * img->width;
570  priv->rlecode = 0; // Who knows what color this should really be
571  priv->bmpflags |= BMP_RLE_ENC;
572  } else { // Absolute mode
573  priv->rlerun = b[1];
574  priv->bmpflags |= BMP_RLE_ABS;
575  }
576  }
577  return len;
578  }
579  #endif
580  #if GDISP_NEED_IMAGE_BMP_8
581  {
582  gU8 b[4];
583 
584  while(x < img->width && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-4) {
585  if (gfileRead(img->f, &b, 4) != 4)
586  return 0;
587 
588  *pc++ = priv->palette[b[0]];
589  *pc++ = priv->palette[b[1]];
590  *pc++ = priv->palette[b[2]];
591  *pc++ = priv->palette[b[3]];
592  len += 4;
593  x += 4;
594  }
595  return len;
596  }
597  #endif
598 #endif
599 
600 #if GDISP_NEED_IMAGE_BMP_16
601  case 16:
602  {
603  gU16 w[2];
604  gColor r, g, b;
605 
606  while(x < img->width && len <= GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE-2) {
607  if (gfileRead(img->f, &w, 4) != 4)
608  return 0;
609  gdispImageMakeLE16(w[0]);
610  gdispImageMakeLE16(w[1]);
611  if (priv->shiftred < 0)
612  r = (gColor)((w[0] & priv->maskred) << -priv->shiftred);
613  else
614  r = (gColor)((w[0] & priv->maskred) >> priv->shiftred);
615  if (priv->shiftgreen < 0)
616  g = (gColor)((w[0] & priv->maskgreen) << -priv->shiftgreen);
617  else
618  g = (gColor)((w[0] & priv->maskgreen) >> priv->shiftgreen);
619  if (priv->shiftblue < 0)
620  b = (gColor)((w[0] & priv->maskblue) << -priv->shiftblue);
621  else
622  b = (gColor)((w[0] & priv->maskblue) >> priv->shiftblue);
623  /* We don't support alpha yet */
624  *pc++ = RGB2COLOR(r, g, b);
625  if (priv->shiftred < 0)
626  r = (gColor)((w[1] & priv->maskred) << -priv->shiftred);
627  else
628  r = (gColor)((w[1] & priv->maskred) >> priv->shiftred);
629  if (priv->shiftgreen < 0)
630  g = (gColor)((w[1] & priv->maskgreen) << -priv->shiftgreen);
631  else
632  g = (gColor)((w[1] & priv->maskgreen) >> priv->shiftgreen);
633  if (priv->shiftblue < 0)
634  b = (gColor)((w[1] & priv->maskblue) << -priv->shiftblue);
635  else
636  b = (gU8)((w[1] & priv->maskblue) >> priv->shiftblue);
637  /* We don't support alpha yet */
638  *pc++ = RGB2COLOR(r, g, b);
639  x += 2;
640  len += 2;
641  }
642  }
643  return len;
644 #endif
645 
646 #if GDISP_NEED_IMAGE_BMP_24
647  case 24:
648  {
649  gU8 b[3];
650 
651  while(x < img->width && len < GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE) {
652  if (gfileRead(img->f, &b, 3) != 3)
653  return 0;
654  *pc++ = RGB2COLOR(b[2], b[1], b[0]);
655  x++;
656  len++;
657  }
658 
659  if (x >= img->width) {
660  // Make sure we have read a multiple of 4 bytes for the line
661  if ((x & 3) && gfileRead(img->f, &b, x & 3) != (x & 3))
662  return 0;
663  }
664  }
665  return len;
666 #endif
667 
668 #if GDISP_NEED_IMAGE_BMP_32
669  case 32:
670  {
671  gU32 dw;
672  gColor r, g, b;
673 
674  while(x < img->width && len < GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE) {
675  if (gfileRead(img->f, &dw, 4) != 4)
676  return 0;
677  gdispImageMakeLE32(dw);
678  if (priv->shiftred < 0)
679  r = (gColor)((dw & priv->maskred) << -priv->shiftred);
680  else
681  r = (gColor)((dw & priv->maskred) >> priv->shiftred);
682  if (priv->shiftgreen < 0)
683  g = (gColor)((dw & priv->maskgreen) << -priv->shiftgreen);
684  else
685  g = (gColor)((dw & priv->maskgreen) >> priv->shiftgreen);
686  if (priv->shiftblue < 0)
687  b = (gColor)((dw & priv->maskblue) << -priv->shiftblue);
688  else
689  b = (gColor)((dw & priv->maskblue) >> priv->shiftblue);
690  /* We don't support alpha yet */
691  *pc++ = RGB2COLOR(r, g, b);
692  x++;
693  len++;
694  }
695  }
696  return len;
697 #endif
698 
699  default:
700  return len;
701  }
702 }
703 
704 gdispImageError gdispImageCache_BMP(gImage *img) {
705  gdispImagePrivate_BMP * priv;
706  gColor * pcs;
707  gColor * pcd;
708  gCoord pos, x, y;
709  gMemSize len;
710 
711  /* If we are already cached - just return OK */
712  priv = (gdispImagePrivate_BMP *)img->priv;
713  if (priv->frame0cache)
714  return GDISP_IMAGE_ERR_OK;
715 
716  /* We need to allocate the cache */
717  len = img->width * img->height * sizeof(gPixel);
718  priv->frame0cache = (gPixel *)gdispImageAlloc(img, len);
719  if (!priv->frame0cache)
720  return GDISP_IMAGE_ERR_NOMEMORY;
721 
722  /* Read the entire bitmap into cache */
723  gfileSetPos(img->f, priv->frame0pos);
724 #if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE
725  priv->rlerun = 0;
726  priv->rlecode = 0;
727 #endif
728 
729  pcs = priv->buf; // This line is just to prevent a compiler warning.
730 
731  if (priv->bmpflags & BMP_TOP_TO_BOTTOM) {
732  for(y = 0, pcd = priv->frame0cache; y < img->height; y++) {
733  x = 0; pos = 0;
734  while(x < img->width) {
735  if (!pos) {
736  if (!(pos = getPixels(img, x)))
737  return GDISP_IMAGE_ERR_BADDATA;
738  pcs = priv->buf;
739  }
740  *pcd++ = *pcs++;
741  x++; pos--;
742  }
743  }
744  } else {
745  for(y = img->height-1, pcd = priv->frame0cache + img->width*(img->height-1); y >= 0; y--, pcd -= 2*img->width) {
746  x = 0; pos = 0;
747  while(x < img->width) {
748  if (!pos) {
749  if (!(pos = getPixels(img, x)))
750  return GDISP_IMAGE_ERR_BADDATA;
751  pcs = priv->buf;
752  }
753  *pcd++ = *pcs++;
754  x++; pos--;
755  }
756  }
757  }
758 
759  return GDISP_IMAGE_ERR_OK;
760 }
761 
762 gdispImageError gdispGImageDraw_BMP(GDisplay *g, gImage *img, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord sx, gCoord sy) {
763  gdispImagePrivate_BMP * priv;
764  gCoord mx, my;
765  gCoord pos, len, st;
766 
767  priv = (gdispImagePrivate_BMP *)img->priv;
768 
769  /* Check some reasonableness */
770  if (sx >= img->width || sy >= img->height) return GDISP_IMAGE_ERR_OK;
771  if (sx + cx > img->width) cx = img->width - sx;
772  if (sy + cy > img->height) cy = img->height - sy;
773 
774  /* Draw from the image cache - if it exists */
775  if (priv->frame0cache) {
776  gdispGBlitArea(g, x, y, cx, cy, sx, sy, img->width, priv->frame0cache);
777  return GDISP_IMAGE_ERR_OK;
778  }
779 
780  /* Start decoding from the beginning */
781  gfileSetPos(img->f, priv->frame0pos);
782 #if GDISP_NEED_IMAGE_BMP_4_RLE || GDISP_NEED_IMAGE_BMP_8_RLE
783  priv->rlerun = 0;
784  priv->rlecode = 0;
785 #endif
786 
787  if (priv->bmpflags & BMP_TOP_TO_BOTTOM) {
788  for(my = 0; my < img->height; my++) {
789  mx = 0;
790  while(mx < img->width) {
791  if (!(pos = getPixels(img, mx)))
792  return GDISP_IMAGE_ERR_BADDATA;
793  if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) {
794  st = mx < sx ? sx - mx : 0;
795  len = pos-st;
796  if (mx+st+len > sx+cx) len = sx+cx-mx-st;
797  if (len == 1)
798  gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]);
799  else
800  gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf);
801  }
802  mx += pos;
803  }
804  }
805  } else {
806  for(my = img->height-1; my >= 0; my--) {
807  mx = 0;
808  while(mx < img->width) {
809  if (!(pos = getPixels(img, mx)))
810  return GDISP_IMAGE_ERR_BADDATA;
811  if (my >= sy && my < sy+cy && mx < sx+cx && mx+pos >= sx) {
812  st = mx < sx ? sx - mx : 0;
813  len = pos-st;
814  if (mx+st+len > sx+cx) len = sx+cx-mx-st;
815  if (len == 1)
816  gdispGDrawPixel(g, x+mx+st-sx, y+my-sy, priv->buf[st]);
817  else
818  gdispGBlitArea(g, x+mx+st-sx, y+my-sy, len, 1, st, 0, pos, priv->buf);
819  }
820  mx += pos;
821  }
822  }
823  }
824 
825  return GDISP_IMAGE_ERR_OK;
826 }
827 
828 gDelay gdispImageNext_BMP(gImage *img) {
829  (void) img;
830 
831  /* No more frames/pages */
832  return gDelayForever;
833 }
834 
835 gU16 gdispImageGetPaletteSize_BMP(gImage *img) {
836  #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8
837  gdispImagePrivate_BMP *priv;
838 
839  priv = (gdispImagePrivate_BMP *)img->priv;
840  if (!priv)
841  return 0;
842 
843  if (!(priv->bmpflags & BMP_PALETTE))
844  return 0;
845 
846  return priv->palsize;
847  #else
848  return 0;
849  #endif
850 }
851 
852 gColor gdispImageGetPalette_BMP(gImage *img, gU16 index) {
853  #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8
854  gdispImagePrivate_BMP *priv;
855 
856  priv = (gdispImagePrivate_BMP *)img->priv;
857  if (!priv)
858  return 0;
859 
860  if (!(priv->bmpflags & BMP_PALETTE))
861  return 0;
862 
863  if (index >= priv->palsize)
864  return 0;
865 
866  return priv->palette[(gU8)index];
867 
868  #else
869  return 0;
870  #endif
871 }
872 
873 gBool gdispImageAdjustPalette_BMP(gImage *img, gU16 index, gColor newColor) {
874  #if GDISP_NEED_IMAGE_BMP_1 || GDISP_NEED_IMAGE_BMP_4 || GDISP_NEED_IMAGE_BMP_8
875  gdispImagePrivate_BMP *priv;
876 
877  priv = (gdispImagePrivate_BMP *)img->priv;
878  if (!priv)
879  return gFalse;
880 
881  if (!(priv->bmpflags & BMP_PALETTE))
882  return gFalse;
883 
884  if (index >= priv->palsize)
885  return gFalse;
886 
887  priv->palette[(gU8)index] = newColor;
888 
889  return gTrue;
890 
891  #else
892  return 0;
893  #endif
894 }
895 
896 #endif /* GFX_USE_GDISP && GDISP_NEED_IMAGE && GDISP_NEED_IMAGE_BMP */
GDISP image support routines header file.
#define RGB2COLOR(r, g, b)
Convert red, green, blue (each 0 to 255) into a color value.
Definition: gdisp_colors.h:196
COLOR_TYPE gColor
The color type definition.
Definition: gdisp_colors.h:437
void gdispGDrawPixel(GDisplay *g, gCoord x, gCoord y, gColor color)
Set a pixel in the specified color.
#define GDISP_NEED_IMAGE_BMP_8_RLE
Is BMP 8 bits per pixel (256 color) with RLE compression image decoding required.
void gdispGBlitArea(GDisplay *g, gCoord x, gCoord y, gCoord cx, gCoord cy, gCoord srcx, gCoord srcy, gCoord srccx, const gPixel *buffer)
Fill an area using the supplied bitmap.
gColor gPixel
The pixel format.
Definition: gdisp.h:226
#define GDISP_NEED_IMAGE_BMP_1
Is BMP 1 bit per pixel (monochrome/2 color) image decoding required.
#define GDISP_IMAGE_BMP_BLIT_BUFFER_SIZE
The BMP blit buffer size in pixels.
#define GDISP_NEED_IMAGE_BMP_8
Is BMP 8 bits per pixel (256 color) image decoding required.
gI16 gCoord
The type for a coordinate or length on the screen.
Definition: gdisp.h:39
#define GDISP_NEED_IMAGE_BMP_4
Is BMP 4 bits per pixel (16 color) image decoding required.
#define GDISP_NEED_IMAGE_BMP_4_RLE
Is BMP 4 bits per pixel (16 color) with RLE compression image decoding required.
gBool gfileSetPos(GFILE *f, gFileSize pos)
Set the position of the read/write cursor.
gMemSize gfileRead(GFILE *f, void *buf, gMemSize len)
Read from file.
gFileSize gfileGetPos(GFILE *f)
Get the current position of the read/write cursor.
gU16 gdispImageError
An image error code.
Definition: gdisp_image.h:37
The structure for an image.
Definition: gdisp_image.h:59