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