80c455d1f5195b104d5607a4b812bf33b5a0527b
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-bmp.cpp
1 /*
2  * Copyright (c) 2022 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17
18 #include <dali/internal/imaging/common/loader-bmp.h>
19
20 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
21 #include <dali/integration-api/debug.h>
22 #include <dali/public-api/common/vector-wrapper.h>
23
24 namespace Dali
25 {
26 namespace TizenPlatform
27 {
28 namespace
29 {
30 const unsigned int FileHeaderOffsetOfBF32V4  = 0x7A;
31 const unsigned int MaskForBFRGB565           = 0x80;
32 const unsigned int FileHeaderOffsetOfRGB24V5 = 0x8A;
33
34 enum BmpFormat
35 {
36   BMP_RGB1 = 14,     //BI_RGB & bpp =1
37   BMP_RGB4,          //BI_RGB & bpp = 4
38   BMP_RGB8,          //BI_RGB & bpp = 8
39   BMP_RGB555,        //BI_RGB & bpp = 16
40   BMP_BITFIELDS555,  //BI_BITFIELDS & 16bit & R:G:B = 5:5:5
41   BMP_BITFIELDS32,   //BI_BITFIELDS & 32bit & R:G:B:A = 8:8:8:8
42   BMP_RLE8,          //BI_RLE8
43   BMP_RLE4,          //BI_RLE4
44   BMP_BITFIELDS32V4, //BI_BITFIELDS & 32bit
45   BMP_RGB24V5,       //BI_RGB & bpp = 24 & bmp version5
46   BMP_NOTEXIST
47 };
48
49 struct BmpFileHeader
50 {
51   unsigned short signature;    // Bitmap file signature
52   unsigned int   fileSize;     // Bitmap file size in bytes
53   unsigned short reserved1;    // Reserved bits
54   unsigned short reserved2;    // Reserved bits
55   unsigned int   offset;       // Offset from BMP file header to BMP bits
56 } __attribute__((__packed__)); // Stops the structure from being aligned to every 4 bytes
57
58 struct BmpInfoHeader
59 {
60   unsigned int   infoHeaderSize;  // Specifies the number of bytes required by the info header
61   unsigned int   width;           // The Image Width
62   int            height;          // The Image Height (negative value represents image data is flipped)
63   unsigned short planes;          // The number of color planes, must be 1
64   unsigned short bitsPerPixel;    // The bits per pixel
65   unsigned int   compression;     // The type of compression used by the image
66   unsigned int   imageSize;       // The size of the image in bytes
67   unsigned int   xPixelsPerMeter; // The number of pixels per meter in x axis
68   unsigned int   yPixelsPerMeter; // The number of pixels per meter in y axis
69   unsigned int   numberOfColors;  // The number of colors in the color table
70   unsigned int   importantColors; // The important color count
71 } __attribute__((__packed__));    // Stops the structure from being aligned to every 4 bytes
72
73 /**
74  * Template function to read from the file directly into our structure.
75  * @param[in]  fp     The file to read from
76  * @param[out] header The structure we want to store our information in
77  * @return true, if read successful, false otherwise
78  */
79 template<typename T>
80 inline bool ReadHeader(FILE* fp, T& header)
81 {
82   const unsigned int readLength = sizeof(T);
83
84   // Load the information directly into our structure
85   if(fread(&header, 1, readLength, fp) != readLength)
86   {
87     return false;
88   }
89
90   return true;
91 }
92
93 bool LoadBmpHeader(FILE* fp, unsigned int& width, unsigned int& height, BmpFileHeader& fileHeader, BmpInfoHeader& infoHeader)
94 {
95   if(!ReadHeader(fp, fileHeader))
96   {
97     DALI_LOG_ERROR("File header read failed\n");
98     return false;
99   }
100
101   if(!ReadHeader(fp, infoHeader))
102   {
103     DALI_LOG_ERROR("Info header read failed\n");
104     return false;
105   }
106
107   width  = infoHeader.width;
108   height = abs(infoHeader.height);
109
110   if(infoHeader.width == 0)
111   {
112     DALI_LOG_ERROR("Invalid header size\n");
113     return false;
114   }
115
116   return true;
117 }
118
119 /**
120  * function to decode format BI_RGB & bpp = 24 & bmp version5.
121  * @param[in]  fp      The file to read from
122  * @param[out] pixels  The pointer that  we want to store bmp data  in
123  * @param[in]  width   bmp width
124  * @param[in]  height  bmp height
125  * @param[in]  offset  offset from bmp header to bmp image data
126  * @param[in]  topDown indicate image data is read from bottom or from top
127  * @param[in]  padding padded to a u_int32 boundary for each line
128  * @return true, if decode successful, false otherwise
129  */
130 bool DecodeRGB24V5(FILE*          fp,
131                    unsigned char* pixels,
132                    unsigned int   width,
133                    unsigned int   height,
134                    unsigned int   offset,
135                    bool           topDown,
136                    unsigned int   rowStride,
137                    unsigned int   padding)
138 {
139   if(fp == NULL || pixels == NULL)
140   {
141     DALI_LOG_ERROR("Error decoding BMP_RGB24V5 format\n");
142     return false;
143   }
144   if(fseek(fp, offset, SEEK_SET))
145   {
146     DALI_LOG_ERROR("Error seeking BMP_RGB24V5 data\n");
147     return false;
148   }
149
150   for(std::uint32_t yPos = 0; yPos < height; ++yPos)
151   {
152     std::uint8_t* pixelsPtr = NULL;
153     if(topDown)
154     {
155       pixelsPtr = pixels + (yPos * rowStride);
156     }
157     else
158     {
159       pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
160     }
161     if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
162     {
163       DALI_LOG_ERROR("Error reading the BMP image\n");
164       return false;
165     }
166     for(std::uint32_t i = 0; i < rowStride; i += 3)
167     {
168       std::uint8_t temp = pixelsPtr[i];
169       pixelsPtr[i]      = pixelsPtr[i + 2];
170       pixelsPtr[i + 2]  = temp;
171     }
172
173     if(padding)
174     {
175       // move past the padding.
176       if(fseek(fp, padding, SEEK_CUR))
177       {
178         DALI_LOG_ERROR("Error moving past BMP_RGB24V5 padding\n");
179       }
180     }
181   }
182   return true;
183 }
184
185 /**
186  * function to decode format BI_BITFIELDS & bpp = 32 & bmp version4.
187  * @param[in]  fp        The file to read from
188  * @param[out] pixels    The pointer that  we want to store bmp data  in
189  * @param[in]  width     bmp width
190  * @param[in]  height    bmp height
191  * @param[in]  offset    offset from bmp header to bmp image data
192  * @param[in]  topDown   indicate image data is read from bottom or from top
193  * @param[in]  rowStride bits span for each line
194  * @param[in]  padding   padded to a u_int32 boundary for each line
195  * @return true, if decode successful, false otherwise
196  */
197 bool DecodeBF32V4(FILE*          fp,
198                   unsigned char* pixels,
199                   unsigned int   width,
200                   unsigned int   height,
201                   unsigned int   offset,
202                   bool           topDown,
203                   unsigned int   rowStride,
204                   unsigned int   padding)
205 {
206   if(fp == NULL || pixels == NULL)
207   {
208     DALI_LOG_ERROR("Error decoding BMP_BITFIELDS32V4 format\n");
209     return false;
210   }
211   if(fseek(fp, offset, SEEK_SET))
212   {
213     DALI_LOG_ERROR("Error seeking BMP_BITFIELDS32V4 data\n");
214     return false;
215   }
216
217   for(std::uint32_t yPos = 0; yPos < height; ++yPos)
218   {
219     std::uint8_t* pixelsPtr = NULL;
220     if(topDown)
221     {
222       pixelsPtr = pixels + (yPos * rowStride);
223     }
224     else
225     {
226       pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
227     }
228     if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
229     {
230       DALI_LOG_ERROR("Error reading the BMP image\n");
231       return false;
232     }
233     for(std::uint32_t i = 0; i < rowStride; i += 4)
234     {
235       std::uint8_t temp = pixelsPtr[i];
236       pixelsPtr[i]      = pixelsPtr[i + 2];
237       pixelsPtr[i + 2]  = temp;
238     }
239     if(padding)
240     {
241       // move past the padding.
242       if(fseek(fp, padding, SEEK_CUR))
243       {
244         DALI_LOG_ERROR("Error moving past BMP_BITFIELDS32V4 padding\n");
245       }
246     }
247   }
248   return true;
249 }
250
251 /**
252  * function to decode format BI_BITFIELDS & bpp = 32
253  * @param[in]  fp        The file to read from
254  * @param[out] pixels    The pointer that  we want to store bmp data  in
255  * @param[in]  width     bmp width
256  * @param[in]  height    bmp height
257  * @param[in]  offset    offset from bmp header to bmp image data
258  * @param[in]  topDown   indicate image data is read from bottom or from top
259  * @param[in]  rowStride bits span for each line
260  * @param[in]  padding   padded to a u_int32 boundary for each line
261  * @return true, if decode successful, false otherwise
262  */
263 bool DecodeBF32(FILE*          fp,
264                 unsigned char* pixels,
265                 unsigned int   width,
266                 unsigned int   height,
267                 unsigned int   offset,
268                 bool           topDown,
269                 unsigned int   rowStride,
270                 unsigned int   padding)
271 {
272   if(fp == NULL || pixels == NULL)
273   {
274     DALI_LOG_ERROR("Error decoding BMP_BITFIELDS32 format\n");
275     return false;
276   }
277   if(fseek(fp, offset, SEEK_SET))
278   {
279     DALI_LOG_ERROR("Error seeking BMP_BITFIELDS32 data\n");
280     return false;
281   }
282
283   for(std::uint32_t yPos = 0; yPos < height; ++yPos)
284   {
285     std::uint8_t* pixelsPtr;
286     if(topDown)
287     {
288       // the data in the file is top down, and we store the data top down
289       pixelsPtr = pixels + (yPos * rowStride);
290     }
291     else
292     {
293       // the data in the file is bottom up, and we store the data top down
294       pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
295     }
296
297     if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
298     {
299       DALI_LOG_ERROR("Error reading the BMP image\n");
300       return false;
301     }
302     for(std::uint32_t i = 0; i < rowStride; i += 4)
303     {
304       std::uint8_t temp = pixelsPtr[i];
305       pixelsPtr[i]      = pixelsPtr[i + 2];
306       pixelsPtr[i + 2]  = temp;
307     }
308
309     if(padding)
310     {
311       // move past the padding.
312       if(fseek(fp, padding, SEEK_CUR))
313       {
314         DALI_LOG_ERROR("Error moving past BMP_BITFIELDS32 padding\n");
315       }
316     }
317   }
318   return true;
319 }
320
321 /**
322  * function to decode format BI_BITFIELDS & bpp = 16 & R:G:B = 5:6:5
323  * @param[in]  fp      The file to read from
324  * @param[out] pixels  The pointer that  we want to store bmp data  in
325  * @param[in]  width   bmp width
326  * @param[in]  height  bmp height
327  * @param[in]  offset  offset from bmp header to bmp image data
328  * @param[in]  topDown indicate image data is read from bottom or from top
329  * @return true, if decode successful, false otherwise
330  */
331 bool DecodeBF565(FILE*          fp,
332                  unsigned char* pixels,
333                  unsigned int   width,
334                  unsigned int   height,
335                  unsigned int   offset,
336                  bool           topDown)
337 {
338   if(fp == NULL || pixels == NULL)
339   {
340     DALI_LOG_ERROR("Error decoding RGB565 format\n");
341     return false;
342   }
343   if(fseek(fp, offset, SEEK_SET))
344   {
345     DALI_LOG_ERROR("Error seeking RGB565 data\n");
346     return false;
347   }
348
349   width                   = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
350   std::uint32_t rowStride = width * 2;
351
352   for(std::uint32_t i = 0; i < height; ++i)
353   {
354     std::uint8_t* pixelsPtr = NULL;
355     if(topDown)
356     {
357       // the data in the file is top down, and we store the data top down
358       pixelsPtr = pixels + (i * rowStride);
359     }
360     else
361     {
362       // the data in the file is bottom up, and we store the data top down
363       pixelsPtr = pixels + (((height - 1) - i) * rowStride);
364     }
365     if(fread(pixelsPtr, 1, rowStride, fp) != rowStride)
366     {
367       return false;
368     }
369   }
370
371   return true;
372 }
373
374 /**
375  * function to decode format BI_BITFIELDS & bpp = 16 & R:G:B = 5:5:5
376  * @param[in]  fp      The file to read from
377  * @param[out] pixels  The pointer that  we want to store bmp data  in
378  * @param[in]  width   bmp width
379  * @param[in]  height  bmp height
380  * @param[in]  offset  offset from bmp header to bmp image data
381  * @param[in]  topDown indicate image data is read from bottom or from top
382  * @return true, if decode successful, false otherwise
383  */
384 bool DecodeBF555(FILE*          fp,
385                  unsigned char* pixels,
386                  unsigned int   width,
387                  unsigned int   height,
388                  unsigned int   offset,
389                  bool           topDown)
390 {
391   if(fp == NULL || pixels == NULL)
392   {
393     DALI_LOG_ERROR("Error decoding BMP_BITFIELDS555 format\n");
394     return false;
395   }
396
397   if(fseek(fp, offset, SEEK_SET))
398   {
399     DALI_LOG_ERROR("Error seeking BMP_BITFIELDS555 data\n");
400     return false;
401   }
402
403   width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
404
405   std::vector<std::uint8_t> raw(width * height * 2);
406   std::uint32_t             rawStride = width * 2;
407   std::uint32_t             rowStride = width * 3;
408
409   std::uint8_t* rawPtr = NULL;
410   for(std::uint32_t j = 0; j < height; ++j)
411   {
412     rawPtr = &raw[0] + (j * rawStride);
413     if(fread(rawPtr, 1, rawStride, fp) != rawStride)
414     {
415       return false;
416     }
417   }
418
419   for(std::uint32_t yPos = 0; yPos < height; ++yPos)
420   {
421     std::uint8_t* pixelsPtr = NULL;
422     if(topDown)
423     {
424       // the data in the file is top down, and we store the data top down
425       pixelsPtr = pixels + (yPos * rowStride);
426     }
427     else
428     {
429       // the data in the file is bottom up, and we store the data top down
430       pixelsPtr = pixels + (((height - 1) - yPos) * rowStride);
431     }
432
433     for(std::uint32_t k = 0; k < width; ++k)
434     {
435       std::uint32_t index  = yPos * rawStride + 2 * k;
436       pixelsPtr[3 * k]     = ((raw[index + 1] >> 2) & 0x1F) * 0xFF / 0x1F;
437       pixelsPtr[3 * k + 1] = (((raw[index + 1] & 0x03) << 3) | (raw[index] >> 5)) * 0xFF / 0x1F;
438       pixelsPtr[3 * k + 2] = (raw[index] & 0x1F) * 0xFF / 0x1F;
439     }
440   }
441   return true;
442 }
443
444 /**
445  * function to decode format BI_RGB & bpp = 16 & R:G:B = 5:5:5
446  * @param[in]  fp      The file to read from
447  * @param[out] pixels  The pointer that  we want to store bmp data  in
448  * @param[in]  width   bmp width
449  * @param[in]  height  bmp height
450  * @param[in]  offset  offset from bmp header to bmp image data
451  * @param[in]  topDown indicate image data is read from bottom or from top
452  * @return true, if decode successful, false otherwise
453  */
454 bool DecodeRGB555(FILE*          fp,
455                   unsigned char* pixels,
456                   unsigned int   width,
457                   unsigned int   height,
458                   unsigned int   offset,
459                   bool           topDown)
460 {
461   if(fp == NULL || pixels == NULL)
462   {
463     DALI_LOG_ERROR("Error decoding BMP_RGB555 format\n");
464     return false;
465   }
466   if(fseek(fp, offset, SEEK_SET))
467   {
468     DALI_LOG_ERROR("Error seeking BMP_RGB555 data\n");
469     return false;
470   }
471
472   width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
473   std::vector<std::uint8_t> raw(width * height * 2);
474   std::uint32_t             rawStride = width * 2;
475   std::uint32_t             rowStride = width * 3;
476
477   std::uint8_t* rawPtr = NULL;
478   for(std::uint32_t j = 0; j < height; ++j)
479   {
480     rawPtr = &raw[0] + (j * rawStride);
481     if(fread(rawPtr, 1, rawStride, fp) != rawStride)
482     {
483       return false;
484     }
485   }
486   for(std::uint32_t i = 0; i < height; ++i)
487   {
488     std::uint8_t* pixelsPtr = NULL;
489     if(topDown)
490     {
491       // the data in the file is top down, and we store the data top down
492       pixelsPtr = pixels + (i * rowStride);
493     }
494     else
495     {
496       // the data in the file is bottom up, and we store the data top down
497       pixelsPtr = pixels + (((height - 1) - i) * rowStride);
498     }
499     for(std::uint32_t k = 0; k < width; ++k)
500     {
501       std::uint32_t index  = i * rawStride + 2 * k;
502       pixelsPtr[3 * k]     = ((raw[index + 1] >> 2) & 0x1F) * 0xFF / 0x1F;
503       pixelsPtr[3 * k + 1] = (((raw[index + 1] & 0x03) << 3) | (raw[index] >> 5)) * 0xFF / 0x1F;
504       pixelsPtr[3 * k + 2] = (raw[index] & 0x1F) * 0xFF / 0x1F;
505     }
506   }
507   return true;
508 }
509
510 /**
511  * function to decode format BI_RGB & bpp = 1
512  * @param[in]  fp      The file to read from
513  * @param[out] pixels  The pointer that  we want to store bmp data  in
514  * @param[in]  width   bmp width
515  * @param[in]  height  bmp height
516  * @param[in]  offset  offset from bmp header to bmp palette data
517  * @param[in]  topDown indicate image data is read from bottom or from top
518  * @return true, if decode successful, false otherwise
519  */
520 bool DecodeRGB1(FILE*          fp,
521                 unsigned char* pixels,
522                 unsigned int   width,
523                 unsigned int   height,
524                 unsigned int   offset,
525                 bool           topDown)
526 {
527   if(fp == NULL || pixels == NULL)
528   {
529     DALI_LOG_ERROR("Error decoding BMP_RGB1 format\n");
530     return false;
531   }
532   if(fseek(fp, offset, SEEK_SET))
533   {
534     DALI_LOG_ERROR("Error seeking BMP_RGB1 data\n");
535     return false;
536   }
537
538   std::uint8_t              colorTable[8] = {0};
539   std::uint8_t              cmd;
540   std::uint32_t             fillw = ((width & 63) != 0) ? width + 64 - (width & 63) : width;
541   std::vector<std::uint8_t> colorIndex(fillw * height);
542   std::uint32_t             rowStride = fillw * 3; // RGB
543
544   if(fread(colorTable, 1, 8, fp) != 8)
545   {
546     return false;
547   }
548
549   for(std::uint32_t i = 0; i < fillw * height; i += 8)
550   {
551     if(fread(&cmd, 1, 1, fp) != 1)
552     {
553       return false;
554     }
555
556     colorIndex[i]     = (cmd >> 7) & 0x01;
557     colorIndex[i + 1] = (cmd >> 6) & 0x01;
558     colorIndex[i + 2] = (cmd >> 5) & 0x01;
559     colorIndex[i + 3] = (cmd >> 4) & 0x01;
560     colorIndex[i + 4] = (cmd >> 3) & 0x01;
561     colorIndex[i + 5] = (cmd >> 2) & 0x01;
562     colorIndex[i + 6] = (cmd >> 1) & 0x01;
563     colorIndex[i + 7] = (cmd & 0x01);
564   }
565
566   for(std::uint32_t index = 0; index < height; ++index)
567   {
568     std::uint8_t* pixelsPtr = NULL;
569     if(topDown)
570     {
571       // the data in the file is top down, and we store the data top down
572       pixelsPtr = pixels + (index * rowStride);
573     }
574     else
575     {
576       // the data in the file is bottom up, and we store the data top down
577       pixelsPtr = pixels + (((height - 1) - index) * rowStride);
578     }
579     for(std::uint32_t j = 0; j < fillw; ++j)
580     {
581       std::uint32_t ctIndex = 0;
582       if((fillw * index + j) < (fillw * height))
583       {
584         ctIndex = colorIndex[fillw * index + j];
585       }
586       else
587       {
588         break;
589       }
590       // temp solution for PLM bug P130411-5268, there is one mono bmp that cause DecodeRGB1 API crash.
591       if(((3 * j + 2) < height * fillw * 3) && (ctIndex < 2))
592       {
593         pixelsPtr[3 * j]     = colorTable[4 * ctIndex + 2];
594         pixelsPtr[3 * j + 1] = colorTable[4 * ctIndex + 1];
595         pixelsPtr[3 * j + 2] = colorTable[4 * ctIndex];
596       }
597     }
598   }
599   return true;
600 }
601
602 /**
603  * function to decode format BI_RGB & bpp = 4
604  * @param[in]  fp      The file to read from
605  * @param[out] pixels  The pointer that  we want to store bmp data  in
606  * @param[in]  width   bmp width
607  * @param[in]  height  bmp height
608  * @param[in]  offset  offset from bmp header to bmp palette data
609  * @param[in]  topDown indicate image data is read from bottom or from top
610  * @return true, if decode successful, false otherwise
611  */
612 bool DecodeRGB4(FILE*          fp,
613                 unsigned char* pixels,
614                 unsigned int   width,
615                 unsigned int   height,
616                 unsigned int   offset,
617                 bool           topDown)
618 {
619   if(fp == NULL || pixels == NULL)
620   {
621     DALI_LOG_ERROR("Error decoding BMP_RGB4 format\n");
622     return false;
623   }
624   if(fseek(fp, offset, SEEK_SET))
625   {
626     DALI_LOG_ERROR("Error seeking BMP_RGB4 data\n");
627     return false;
628   }
629
630   std::uint8_t              colorTable[64];
631   std::uint8_t              cmd;
632   std::uint32_t             fillw = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
633   std::vector<std::uint8_t> colorIndex(fillw * height);
634   std::uint32_t             rowStride = fillw * 3;
635
636   if(fread(colorTable, 1, 64, fp) != 64)
637   {
638     return false;
639   }
640
641   for(std::uint32_t i = 0; i < fillw * height; i += 2)
642   {
643     if(fread(&cmd, 1, 1, fp) != 1)
644     {
645       return false;
646     }
647
648     colorIndex[i]     = cmd >> 4;
649     colorIndex[i + 1] = cmd & (0x0F);
650   }
651   std::uint32_t ctIndex = 0;
652
653   for(std::uint32_t index = 0; index < height; ++index)
654   {
655     std::uint8_t* pixelsPtr = NULL;
656     if(topDown)
657     {
658       // the data in the file is top down, and we store the data top down
659       pixelsPtr = pixels + (index * rowStride);
660     }
661     else
662     {
663       // the data in the file is bottom up, and we store the data top down
664       pixelsPtr = pixels + (((height - 1) - index) * rowStride);
665     }
666     for(std::uint32_t j = 0; j < fillw; ++j)
667     {
668       ctIndex                = colorIndex[fillw * index + j];
669       pixelsPtr[3 * j]       = colorTable[4 * ctIndex + 2];
670       pixelsPtr[(3 * j + 1)] = colorTable[4 * ctIndex + 1];
671       pixelsPtr[(3 * j + 2)] = colorTable[4 * ctIndex];
672     }
673   }
674
675   return true;
676 }
677
678 /**
679  * function to decode format BI_RGB & bpp = 8
680  * @param[in]  fp      The file to read from
681  * @param[out] pixels  The pointer that  we want to store bmp data  in
682  * @param[in]  width   bmp width
683  * @param[in]  height  bmp height
684  * @param[in]  offset  offset from bmp header to bmp palette data
685  * @param[in]  topDown indicate image data is read from bottom or from top
686  * @return true, if decode successful, false otherwise
687  */
688 bool DecodeRGB8(FILE*          fp,
689                 unsigned char* pixels,
690                 unsigned int   width,
691                 unsigned int   height,
692                 unsigned int   offset,
693                 bool           topDown)
694 {
695   if(fp == NULL || pixels == NULL)
696   {
697     DALI_LOG_ERROR("Error decoding BMP_RGB8 format\n");
698     return false;
699   }
700   if(fseek(fp, offset, SEEK_SET))
701   {
702     DALI_LOG_ERROR("Error seeking BMP_RGB8 data\n");
703     return false;
704   }
705
706   std::vector<std::uint8_t> colorTable(1024);
707   width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
708   std::vector<std::uint8_t> colorIndex(width * height);
709   std::uint32_t             rowStride = width * 3; //RGB8->RGB24
710
711   if(fread(&colorTable[0], 1, 1024, fp) != 1024)
712   {
713     return false;
714   }
715   if(fread(&colorIndex[0], 1, width * height, fp) != width * height)
716   {
717     return false;
718   }
719   std::uint8_t ctIndex = 0;
720   for(std::uint32_t index = 0; index < height; ++index)
721   {
722     std::uint8_t* pixelsPtr = NULL;
723     if(topDown)
724     {
725       // the data in the file is top down, and we store the data top down
726       pixelsPtr = pixels + (index * rowStride);
727     }
728     else
729     {
730       // the data in the file is bottom up, and we store the data top down
731       pixelsPtr = pixels + (((height - 1) - index) * rowStride);
732     }
733     for(std::uint8_t j = 0; j < width; ++j)
734     {
735       ctIndex                = colorIndex[width * index + j];
736       pixelsPtr[3 * j]       = colorTable[4 * ctIndex + 2];
737       pixelsPtr[(3 * j + 1)] = colorTable[4 * ctIndex + 1];
738       pixelsPtr[(3 * j + 2)] = colorTable[4 * ctIndex];
739     }
740   }
741   return true;
742 }
743
744 /**
745  * function to decode format BI_RLE4 & bpp = 4
746  * @param[in]  fp      The file to read from
747  * @param[out] pixels  The pointer that  we want to store bmp data  in
748  * @param[in]  width   bmp width
749  * @param[in]  height  bmp height
750  * @param[in]  offset  offset from bmp header to bmp palette data
751  * @param[in]  topDown indicate image data is read from bottom or from top
752  * @return true, if decode successful, false otherwise
753  */
754 bool DecodeRLE4(FILE*          fp,
755                 unsigned char* pixels,
756                 unsigned int   width,
757                 unsigned int   height,
758                 unsigned int   offset,
759                 bool           topDown)
760 {
761   if(fp == NULL || pixels == NULL)
762   {
763     DALI_LOG_ERROR("Error decoding BMP_RLE4 format\n");
764     return false;
765   }
766   std::uint8_t* pixelsPtr = pixels;
767   width                   = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
768   std::uint8_t              cmd[2];
769   std::uint32_t             cmdStride = 2;
770   std::uint8_t              colorTable[64];
771   std::vector<std::uint8_t> colorIndex(width * height >> 1);
772   std::vector<std::uint8_t> run;
773   std::uint32_t             x  = 0;
774   std::uint32_t             y  = 0;
775   std::uint32_t             dx = 0;
776   std::uint32_t             dy = 0;
777   width += (width & 1);
778   width = width >> 1;
779
780   bool finish = false;
781
782   if(fseek(fp, offset, SEEK_SET))
783   {
784     DALI_LOG_ERROR("Error seeking BMP_RLE4 data\n");
785     return false;
786   }
787
788   if(fread(colorTable, 1, 64, fp) != 64)
789   {
790     return false;
791   }
792
793   while((x >> 1) + y * width < width * height)
794   {
795     if(finish)
796     {
797       break;
798     }
799     if(fread(cmd, 1, cmdStride, fp) != cmdStride)
800     {
801       return false;
802     }
803     if(cmd[0] == 0) // ESCAPE
804     {
805       switch(cmd[1])
806       {
807         case 1: //end of bitmap
808           finish = true;
809           break;
810         case 0: // end of line
811           x = 0;
812           y++;
813           break;
814         case 2: // delta
815           if(fread(cmd, 1, cmdStride, fp) != cmdStride)
816           {
817             DALI_LOG_ERROR("Error reading the BMP image\n");
818             return false;
819           }
820           dx = cmd[0] & (0xFF);
821           dy = cmd[1] & (0xFF);
822           x += dx;
823           y += dy;
824           break;
825         default:
826           // decode a literal run
827           std::uint32_t length = cmd[1] & (0xFF);
828           //size of run, which is word aligned
829           std::uint32_t bytesize = length;
830           bytesize += (bytesize & 1);
831           bytesize >>= 1;
832           bytesize += (bytesize & 1);
833           run.resize(bytesize);
834           if(fread(&run[0], 1, bytesize, fp) != bytesize)
835           {
836             DALI_LOG_ERROR("Error reading the BMP image\n");
837             return false;
838           }
839           if((x & 1) == 0)
840           {
841             length += (length & 1);
842             length >>= 1;
843             for(std::uint32_t i = 0; i < length; ++i)
844             {
845               colorIndex[(x >> 1) + width * (height - y - 1) + i] = run[i];
846             }
847           }
848           else
849           {
850             for(std::uint32_t i = 0; i < length; ++i)
851             {
852               if((i & 1) == 0) //copy high to low
853               {
854                 colorIndex[((x + i) >> 1) + width * (height - y - 1)] |= ((run[i >> 1] & 0xF0) >> 4);
855               }
856               else //copy low to high
857               {
858                 colorIndex[((x + i) >> 1) + width * (height - y - 1)] |= ((run[i >> 1] & 0x0F) << 4);
859               }
860             }
861           }
862           x += cmd[1] & (0xFF);
863           break;
864       }
865     }
866     else
867     {
868       std::uint32_t length = cmd[0] & (0xFF);
869       if((x & 1) == 0)
870       {
871         length += (length & 1);
872         length >>= 1;
873         for(std::uint32_t i = 0; i < length; ++i)
874         {
875           colorIndex[(height - y - 1) * width + i + (x >> 1)] = cmd[1];
876         }
877       }
878       else
879       {
880         for(std::uint32_t i = 0; i < length; ++i)
881         {
882           if((i & 1) == 0)
883           {
884             colorIndex[((x + i) >> 1) + width * (height - y - 1)] |= ((cmd[1] & 0xF0) >> 4);
885           }
886           else
887           {
888             colorIndex[((x + i) >> 1) + width * (height - y - 1)] |= ((cmd[1] & 0x0F) << 4);
889           }
890         }
891       }
892       x += cmd[0] & (0xFF);
893     }
894   }
895
896   std::uint32_t ctIndexHigh = 0;
897   std::uint32_t ctIndexLow  = 0;
898   for(std::uint32_t index = 0; index < (width * height); ++index)
899   {
900     ctIndexHigh              = colorIndex[index] >> 4;
901     ctIndexLow               = colorIndex[index] & (0x0F);
902     pixelsPtr[6 * index]     = colorTable[4 * ctIndexHigh + 2];
903     pixelsPtr[6 * index + 1] = colorTable[4 * ctIndexHigh + 1];
904     pixelsPtr[6 * index + 2] = colorTable[4 * ctIndexHigh];
905     pixelsPtr[6 * index + 3] = colorTable[4 * ctIndexLow + 2];
906     pixelsPtr[6 * index + 4] = colorTable[4 * ctIndexLow + 1];
907     pixelsPtr[6 * index + 5] = colorTable[4 * ctIndexLow];
908   }
909   return true;
910 }
911
912 /**
913  * function to decode format BI_RLE8 & bpp = 8
914  * @param[in]  fp      The file to read from
915  * @param[out] pixels  The pointer that  we want to store bmp data  in
916  * @param[in]  width   bmp width
917  * @param[in]  height  bmp height
918  * @param[in]  offset  offset from bmp header to bmp palette data
919  * @param[in]  topDown indicate image data is read from bottom or from top
920  * @return true, if decode successful, false otherwise
921  */
922 bool DecodeRLE8(FILE*          fp,
923                 unsigned char* pixels,
924                 unsigned int   width,
925                 unsigned int   height,
926                 unsigned int   offset,
927                 bool           topDown)
928 {
929   if(fp == NULL || pixels == NULL)
930   {
931     DALI_LOG_ERROR("Error decoding BMP_RLE8 format\n");
932     return false;
933   }
934   std::uint8_t* pixelsPtr = pixels;
935   std::uint32_t x         = 0;
936   std::uint32_t y         = 0;
937   std::uint32_t cmdStride = 2;
938
939   width = ((width & 3) != 0) ? width + 4 - (width & 3) : width;
940   std::vector<std::uint8_t> colorTable(1024);
941   std::uint8_t              cmd[2];
942   std::vector<std::uint8_t> colorIndex(width * height);
943
944   if(fseek(fp, offset, SEEK_SET))
945   {
946     DALI_LOG_ERROR("Error seeking BMP_RLE8 data\n");
947     return false;
948   }
949
950   if(fread(&colorTable[0], 1, 1024, fp) != 1024)
951   {
952     return false;
953   }
954
955   std::uint32_t             dx         = 0;
956   std::uint32_t             dy         = 0;
957   bool                      finish     = false;
958   std::uint32_t             length     = 0;
959   std::uint32_t             copylength = 0;
960   std::vector<std::uint8_t> run;
961   while((x + y * width) < width * height)
962   {
963     if(finish)
964     {
965       break;
966     }
967     if(fread(cmd, 1, cmdStride, fp) != cmdStride)
968     {
969       return false;
970     }
971
972     if(cmd[0] == 0) //ESCAPE
973     {
974       switch(cmd[1])
975       {
976         case 1: // end of bitmap
977           finish = true;
978           break;
979         case 0: // end of line
980           x = 0;
981           y++;
982           break;
983         case 2: // delta
984           if(fread(cmd, 1, cmdStride, fp) != cmdStride)
985           {
986             DALI_LOG_ERROR("Error reading the BMP image\n");
987             return false;
988           }
989           dx = cmd[0] & (0xFF);
990           dy = cmd[1] & (0xFF);
991           x += dx;
992           y += dy;
993           break;
994         default:
995           //decode a literal run
996           length     = cmd[1] & (0xFF);
997           copylength = length;
998           //absolute mode must be word-aligned
999           length += (length & 1);
1000           run.resize(length);
1001           if(fread(&run[0], 1, length, fp) != length)
1002           {
1003             DALI_LOG_ERROR("Error reading the BMP image\n");
1004             return false;
1005           }
1006
1007           for(std::uint32_t i = 0; i < length; ++i)
1008           {
1009             colorIndex[x + width * (height - y - 1) + i] = run[i];
1010           }
1011           x += copylength;
1012           break;
1013       }
1014     } // end if cmd[0] ==
1015     else
1016     {
1017       length = cmd[0] & (0xFF);
1018       for(std::uint32_t i = 0; i < length; ++i)
1019       {
1020         colorIndex[(height - y - 1) * width + x] = cmd[1];
1021         x++;
1022       }
1023     }
1024   }
1025   std::uint32_t ctIndex = 0;
1026   for(std::uint32_t index = 0; index < width * height; ++index)
1027   {
1028     ctIndex                  = colorIndex[index];
1029     pixelsPtr[3 * index]     = colorTable[4 * ctIndex + 2];
1030     pixelsPtr[3 * index + 1] = colorTable[4 * ctIndex + 1];
1031     pixelsPtr[3 * index + 2] = colorTable[4 * ctIndex];
1032   }
1033   return true;
1034 }
1035
1036 } // unnamed namespace
1037
1038 bool LoadBmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
1039 {
1040   BmpFileHeader fileHeader;
1041   BmpInfoHeader infoHeader;
1042
1043   bool ret = LoadBmpHeader(input.file, width, height, fileHeader, infoHeader);
1044
1045   return ret;
1046 }
1047
1048 bool LoadBitmapFromBmp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
1049 {
1050   //DALI_ASSERT_DEBUG( bitmap.GetPackedPixelsProfile() != 0 && "Need a packed pixel bitmap to load into." );
1051   FILE* const fp = input.file;
1052   if(fp == NULL)
1053   {
1054     DALI_LOG_ERROR("Error loading bitmap\n");
1055     return false;
1056   }
1057   BmpFormat     customizedFormat = BMP_NOTEXIST;
1058   BmpFileHeader fileHeader;
1059   BmpInfoHeader infoHeader;
1060
1061   // Load the header info
1062   unsigned int width, height;
1063
1064   if(!LoadBmpHeader(fp, width, height, fileHeader, infoHeader))
1065   {
1066     return false;
1067   }
1068
1069   Pixel::Format pixelFormat = Pixel::RGB888;
1070   switch(infoHeader.compression)
1071   {
1072     case 0:
1073       switch(infoHeader.bitsPerPixel)
1074       {
1075         case 32:
1076           pixelFormat = Pixel::BGR8888;
1077           break;
1078
1079         case 24:
1080           if(fileHeader.offset == FileHeaderOffsetOfRGB24V5) //0x8A
1081           {
1082             customizedFormat = BMP_RGB24V5;
1083           }
1084           else
1085           {
1086             pixelFormat = Pixel::RGB888;
1087           }
1088           break;
1089
1090         case 16:
1091           customizedFormat = BMP_RGB555;
1092           break;
1093
1094         case 8:
1095           customizedFormat = BMP_RGB8;
1096           break;
1097
1098         case 4: // RGB4
1099           customizedFormat = BMP_RGB4;
1100           break;
1101
1102         case 1: //RGB1
1103           customizedFormat = BMP_RGB1;
1104           break;
1105         default:
1106           DALI_LOG_ERROR("%d bits per pixel not supported for BMP files\n", infoHeader.bitsPerPixel);
1107           return false;
1108       }
1109       break;
1110     case 1: //// RLE8
1111     {
1112       if(infoHeader.bitsPerPixel == 8)
1113       {
1114         customizedFormat = BMP_RLE8;
1115       }
1116       break;
1117     }
1118     case 2: // RLE4
1119     {
1120       if(infoHeader.bitsPerPixel == 4)
1121       {
1122         customizedFormat = BMP_RLE4;
1123       }
1124       break;
1125     }
1126     case 3: // // BI_BITFIELDS
1127     {
1128       if(infoHeader.bitsPerPixel == 16)
1129       {
1130         if(fseek(fp, 14 + infoHeader.infoHeaderSize + 1, SEEK_SET))
1131         {
1132           return false;
1133         }
1134
1135         char mask;
1136         if(fread(&mask, 1, 1, fp) != 1)
1137         {
1138           return false;
1139         }
1140
1141         if((mask & 0x80) == MaskForBFRGB565) // mask is 0xF8
1142         {
1143           pixelFormat = Pixel::RGB565;
1144         }
1145         else if((mask & 0x80) == 0) // mask is 0x 7C
1146         {
1147           customizedFormat = BMP_BITFIELDS555;
1148         }
1149         else
1150         {
1151           return false;
1152         }
1153       }
1154       else if(infoHeader.bitsPerPixel == 32)
1155       {
1156         if(fileHeader.offset == FileHeaderOffsetOfBF32V4) // 0x7A
1157         {
1158           customizedFormat = BMP_BITFIELDS32V4;
1159         }
1160         else
1161         {
1162           customizedFormat = BMP_BITFIELDS32;
1163         }
1164       }
1165       break;
1166     }
1167     default:
1168       DALI_LOG_ERROR("Compression not supported for BMP files\n");
1169       return false;
1170   }
1171
1172   bool topDown = false;
1173
1174   // if height is negative, bitmap data is top down
1175   if(infoHeader.height < 0)
1176   {
1177     infoHeader.height = abs(infoHeader.height);
1178     height            = infoHeader.height;
1179     topDown           = true;
1180   }
1181
1182   unsigned int rowStride = infoHeader.width * (infoHeader.bitsPerPixel >> 3);
1183
1184   // bitmaps row stride is padded to 4 bytes
1185   unsigned int padding = (rowStride % 4);
1186   if(padding)
1187   {
1188     padding = 4 - padding;
1189   }
1190
1191   int  imageW         = infoHeader.width;
1192   int  pixelBufferW   = infoHeader.width;
1193   int  pixelBufferH   = infoHeader.height;
1194   auto newPixelFormat = Pixel::Format::INVALID;
1195
1196   switch(customizedFormat)
1197   {
1198     case BMP_RLE8:
1199     case BMP_RGB8:
1200     case BMP_RGB4:
1201     case BMP_RLE4:
1202     case BMP_RGB555:
1203     case BMP_BITFIELDS555:
1204     {
1205       pixelBufferW   = ((imageW & 3) != 0) ? imageW + 4 - (imageW & 3) : imageW;
1206       pixelBufferH   = abs(infoHeader.height);
1207       newPixelFormat = Pixel::RGB888;
1208       break;
1209     }
1210     case BMP_RGB1:
1211     {
1212       pixelBufferW   = ((imageW & 63) != 0) ? imageW + 64 - (imageW & 63) : imageW;
1213       pixelBufferH   = abs(infoHeader.height);
1214       newPixelFormat = Pixel::RGB888;
1215       break;
1216     }
1217     case BMP_BITFIELDS32:
1218     case BMP_BITFIELDS32V4:
1219     {
1220       pixelBufferH   = abs(infoHeader.height);
1221       newPixelFormat = Pixel::RGB8888;
1222       break;
1223     }
1224     case BMP_RGB24V5:
1225     {
1226       newPixelFormat = Pixel::RGB888;
1227       break;
1228     }
1229     default:
1230       if(pixelFormat == Pixel::RGB565)
1231       {
1232         pixelBufferW   = ((imageW & 3) != 0) ? imageW + 4 - (imageW & 3) : imageW;
1233         pixelBufferH   = abs(infoHeader.height);
1234         newPixelFormat = Pixel::RGB565;
1235       }
1236       else
1237       {
1238         pixelBufferW   = infoHeader.width;
1239         pixelBufferH   = infoHeader.height;
1240         newPixelFormat = pixelFormat;
1241       }
1242       break;
1243   }
1244
1245   bitmap      = Dali::Devel::PixelBuffer::New(pixelBufferW, pixelBufferH, newPixelFormat);
1246   auto pixels = bitmap.GetBuffer();
1247
1248   // Read the raw bitmap data
1249   decltype(pixels) pixelsIterator = nullptr;
1250
1251   bool decodeResult(false);
1252   switch(customizedFormat)
1253   {
1254     case BMP_RGB1:
1255     {
1256       decodeResult = DecodeRGB1(fp, pixels, infoHeader.width, abs(infoHeader.height), 14 + infoHeader.infoHeaderSize, topDown);
1257       break;
1258     }
1259     case BMP_RGB4:
1260     {
1261       decodeResult = DecodeRGB4(fp, pixels, infoHeader.width, abs(infoHeader.height), 14 + infoHeader.infoHeaderSize, topDown);
1262       break;
1263     }
1264     case BMP_RLE4:
1265     {
1266       decodeResult = DecodeRLE4(fp, pixels, infoHeader.width, abs(infoHeader.height), 14 + infoHeader.infoHeaderSize, topDown);
1267       break;
1268     }
1269     case BMP_BITFIELDS32:
1270     {
1271       decodeResult = DecodeBF32(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown, rowStride, padding);
1272       break;
1273     }
1274     case BMP_BITFIELDS555:
1275     {
1276       decodeResult = DecodeBF555(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown);
1277       break;
1278     }
1279     case BMP_RGB555:
1280     {
1281       decodeResult = DecodeRGB555(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown);
1282       break;
1283     }
1284     case BMP_RGB8:
1285     {
1286       decodeResult = DecodeRGB8(fp, pixels, infoHeader.width, abs(infoHeader.height), 14 + infoHeader.infoHeaderSize, topDown);
1287       break;
1288     }
1289     case BMP_RLE8:
1290     {
1291       decodeResult = DecodeRLE8(fp, pixels, infoHeader.width, abs(infoHeader.height), 14 + infoHeader.infoHeaderSize, topDown);
1292       break;
1293     }
1294     case BMP_RGB24V5:
1295     {
1296       decodeResult = DecodeRGB24V5(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown, rowStride, padding);
1297       break;
1298     }
1299     case BMP_BITFIELDS32V4:
1300     {
1301       decodeResult = DecodeBF32V4(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown, rowStride, padding);
1302       break;
1303     }
1304     default:
1305     {
1306       if(pixelFormat == Pixel::RGB565)
1307       {
1308         decodeResult = DecodeBF565(fp, pixels, infoHeader.width, abs(infoHeader.height), fileHeader.offset, topDown);
1309       }
1310       else
1311       {
1312         for(unsigned int yPos = 0; yPos < height; yPos++)
1313         {
1314           if(topDown)
1315           {
1316             // the data in the file is top down, and we store the data top down
1317             pixelsIterator = pixels + (yPos * rowStride);
1318           }
1319           else
1320           {
1321             // the data in the file is bottom up, and we store the data top down
1322             pixelsIterator = pixels + (((height - 1) - yPos) * rowStride);
1323           }
1324
1325           if(fread(pixelsIterator, 1, rowStride, fp) != rowStride)
1326           {
1327             DALI_LOG_ERROR("Error reading the BMP image\n");
1328             break;
1329           }
1330
1331           // If 24 bit mode then swap Blue and Red pixels
1332           // BGR888 doesn't seem to be supported by dali-core
1333           if(infoHeader.bitsPerPixel == 24)
1334           {
1335             for(unsigned int i = 0; i < rowStride; i += 3)
1336             {
1337               unsigned char temp    = pixelsIterator[i];
1338               pixelsIterator[i]     = pixelsIterator[i + 2];
1339               pixelsIterator[i + 2] = temp;
1340             }
1341           }
1342
1343           if(padding)
1344           {
1345             if(fseek(fp, padding, SEEK_CUR)) // move past the padding.
1346             {
1347               DALI_LOG_ERROR("Error moving past BMP padding\n");
1348             }
1349           }
1350         }
1351         decodeResult = true;
1352       }
1353       break;
1354     }
1355   } // switch
1356
1357   if(!decodeResult)
1358   {
1359     DALI_LOG_ERROR("Decoding failed\n");
1360     return false;
1361   }
1362
1363   return true;
1364 }
1365
1366 } // namespace TizenPlatform
1367
1368 } // namespace Dali