Merge "(Environment Options) Reduce cyclomatic complexity of ParseEnvironmentOptions...
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-ico.cpp
1 /*
2  * Copyright (c) 2020 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  * Derived from Enlightenment file evas_image_load_ico.c[1]  which is licensed
19  * under the BSD 2-clause license[2] reproduced below.
20  *
21  * [1][http://web.archive.org/web/20141201151111/http://git.enlightenment.org/core/efl.git/tree/src/modules/evas/loaders/ico/evas_image_load_ico.c]
22  * [2][http://web.archive.org/web/20140717012400/https://git.enlightenment.org/core/efl.git/about/]
23  *
24  * Copyright (C) 2002-2012 Carsten Haitzler, Dan Sinclair, Mike Blumenkrantz,
25  * Samsung Electronics and various contributors (see AUTHORS)
26  *
27  * All rights reserved.
28  *
29  * Redistribution and use in source and binary forms, with or without
30  * modification, are permitted provided that the following conditions are met:
31  *
32  *    1. Redistributions of source code must retain the above copyright
33  *       notice, this list of conditions and the following disclaimer.
34  *    2. Redistributions in binary form must reproduce the above copyright
35  *       notice, this list of conditions and the following disclaimer in the
36  *       documentation and/or other materials provided with the distribution.
37  *
38  * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
39  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
40  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
41  * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
42  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
43  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
44  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
45  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
46  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
47  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48  */
49
50 // HEADER
51 #include <dali/internal/imaging/common/loader-ico.h>
52
53 // EXTERNAL INCLUDES
54 #include <cstring>
55 #include <dali/public-api/common/dali-vector.h>
56
57 // INTERNAL INCLUDES
58 #include <dali/integration-api/debug.h>
59 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
60
61 namespace Dali
62 {
63
64 namespace TizenPlatform
65 {
66
67 namespace
68 {
69 // Reserved 2 bytes + type 2 bytes + count 2 bytes + count * 16 bytes
70 const unsigned char ICO_FILE_HEADER = 22;
71 // Info header 40 bytes = size 4 bytes + width 4 bytes + height 4 bytes + planes 2 bytes + bitcount 2 bytes
72 // + compression 4 bytes + imagesize 4 bytes + xpixelsPerM 4 bytes + ypixelsPerM 4 bytes + colorsUsed 4 bytes + colorImportant 4 bytes
73 // besides, there are rgba color data = numberOfColors * 4 bytes
74 const unsigned char ICO_IMAGE_INFO_HEADER = 40;
75
76 typedef unsigned char  DATA8;
77 #define A_VAL(p) (reinterpret_cast< DATA8 * >( p )[3])
78
79 #define RGB_JOIN(r,g,b) \
80                 (((r) << 16) + ((g) << 8) + (b))
81
82 #define ARGB_JOIN(a,r,g,b) \
83                 (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
84
85 bool read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret)
86 {
87   unsigned char b[2];
88
89   if (*position + 2 > length)
90   {
91     return false;
92   }
93   b[0] = map[(*position)++];
94   b[1] = map[(*position)++];
95   *ret = (b[1] << 8) | b[0];
96   return true;
97 }
98
99 bool read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret)
100 {
101   unsigned char b[4];
102   unsigned int i;
103
104   if (*position + 4 > length)
105   {
106     return false;
107   }
108   for (i = 0; i < 4; i++)
109   {
110     b[i] = map[(*position)++];
111   }
112   *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]);
113   return true;
114 }
115
116 bool read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret)
117 {
118   if (*position + 1 > length)
119   {
120     return false;
121   }
122   *ret = map[(*position)++];
123   return true;
124 }
125
126 bool read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size)
127 {
128   if (*position + size > length)
129   {
130     return false;
131   }
132   memcpy(buffer, map + *position, size);
133   *position += size;
134   return true;
135 }
136
137 enum
138 {
139   SMALLEST,
140   BIGGEST,
141   SMALLER,
142   BIGGER
143 };
144
145 enum
146 {
147   ICON = 1,
148   CURSOR = 2
149 };
150
151 struct IcoData
152 {
153   int pdelta;
154   int w, h;
155   int cols;
156   int bpp, planes;
157   int hot_x, hot_y;
158   unsigned int bmoffset, bmsize;
159 };
160
161 bool LoadIcoHeaderHelper( FILE* fp,
162                           IcoData& chosen,
163                           Dali::Vector<unsigned char>& map,
164                           unsigned int& fsize )
165 {
166   memset( &chosen, 0, sizeof(chosen) );
167
168   if(fp == NULL)
169   {
170     DALI_LOG_ERROR("Error loading bitmap\n");
171     return false;
172   }
173   size_t position = 0;
174   unsigned short word;
175   unsigned char byte;
176
177   if( fseek(fp,0,SEEK_END) )
178   {
179     DALI_LOG_ERROR("Error seeking ICO data\n");
180     return false;
181   }
182
183   long positionIndicator = ftell(fp);
184   fsize = 0u;
185
186   if( positionIndicator > -1L )
187   {
188     fsize = static_cast<unsigned int>(positionIndicator);
189   }
190
191   if( 0u == fsize )
192   {
193     return false;
194   }
195
196   if( fseek(fp, 0, SEEK_SET) )
197   {
198     DALI_LOG_ERROR("Error seeking ICO data\n");
199     return false;
200   }
201
202   if (fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
203   {
204     return false;
205   }
206   map.Resize(fsize);
207
208   if(fread(&map[0], 1, fsize, fp) != fsize)
209   {
210     DALI_LOG_WARNING("image file read opeation error!\n");
211     return false;
212   }
213
214   int search = BIGGEST;
215   unsigned short reserved, type, count;
216   if (!read_ushort(&map[0], fsize, &position, &reserved))
217   {
218     return false;
219   }
220   if (!read_ushort(&map[0], fsize, &position, &type))
221   {
222     return false;
223   }
224   if (!read_ushort(&map[0], fsize, &position, &count))
225   {
226     return false;
227   }
228   if (!((reserved == 0) &&
229        ((type == ICON) || (type == CURSOR)) && (count != 0)))
230   {
231     return false;
232   }
233   search = BIGGEST;
234   chosen.pdelta = 0;
235   bool have_choice = false;
236
237   for (unsigned short i = 0; i < count; i++)
238   {
239     unsigned char tw = 0, th = 0, tcols = 0;
240     if (!read_uchar(&map[0], fsize, &position, &tw))
241     {
242       return false;
243     }
244     int w = tw;
245     if (w <= 0)
246     {
247       w = 256;
248     }
249     if (!read_uchar(&map[0], fsize, &position, &th))
250     {
251       return false;
252
253     }
254     int h = th;
255     if (h <= 0)
256     {
257       h = 256;
258     }
259     if (!read_uchar(&map[0], fsize, &position, &tcols))
260     {
261       return false;
262     }
263     int cols = tcols;
264     if (!read_uchar(&map[0], fsize, &position, &byte))
265     {
266       return false;
267     }
268     if (!read_ushort(&map[0], fsize, &position, &word))
269     {
270       return false;
271     }
272     int planes=0;
273     if (type == 1)
274     {
275       planes = word;
276     }
277     //else hot_x = word;
278     if (!read_ushort(&map[0], fsize, &position, &word))
279     {
280       return false;
281     }
282     int bpp=0;
283     if (type == 1)
284     {
285       bpp = word;
286     }
287
288     // 0 colors means 256 for paletized modes.
289     // Note: We must not do this conversion for bpp greater than 8, as there is no palette.
290     if( bpp <= 8 && cols == 0 )
291     {
292       cols = 256;
293     }
294
295     //else hot_y = word;
296     unsigned int bmoffset, bmsize;
297     if (!read_uint(&map[0], fsize, &position, &bmsize))
298     {
299       return false;
300     }
301     if (!read_uint(&map[0], fsize, &position, &bmoffset))
302     {
303       return false;
304     }
305     if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
306     {
307       return false;
308     }
309     if (search == BIGGEST)
310     {
311       int pdelta = w * h;
312       if ((!have_choice) ||
313        ((pdelta >= chosen.pdelta) &&
314            (((bpp >= 3) && (bpp >= chosen.bpp)) ||
315                ((bpp < 3) && (cols >= chosen.cols)))))
316       {
317         have_choice = true;
318         chosen.pdelta = pdelta;
319         chosen.w = w;
320         chosen.h = h;
321         chosen.cols = cols;
322         chosen.bpp = bpp;
323         chosen.planes = planes;
324         chosen.bmsize = bmsize;
325         chosen.bmoffset = bmoffset;
326       }
327     }
328   }
329
330   if (chosen.bmoffset == 0)
331   {
332     return false;
333   }
334
335   return true;
336 }
337
338 /**
339  * @brief Handle the different bits per pixel
340  * @param[in] bitcount The bit count
341  * @param[in/out] map The map to use/set
342  * @param[in/out] pix A reference to the pointer to the pix buffer
343  * @param[in/out] surface A reference to the surface buffer
344  * @param[in] width The width
345  * @param[in] height The height
346  * @param[in] fsize The file size
347  * @param[in/out] position The position in the file
348  * @param[/outin] pixbuf A reference to the pixbuf
349  * @param[in] stride The stride to use
350  * @param[in] palette The palette
351  */
352 bool HandleBitsPerPixel(
353     const unsigned int bitcount,
354     Dali::Vector<unsigned char>& map,
355     unsigned int*& pix,
356     Dali::Vector<unsigned int>& surface,
357     const unsigned int width,
358     const unsigned int height,
359     const unsigned int fsize,
360     size_t& position,
361     Dali::Vector<unsigned char>& pixbuf,
362     const unsigned int stride,
363     const Dali::Vector<unsigned int>& palette)
364 {
365   // Note: Switch is in order of most common format first.
366   switch( bitcount )
367   {
368     case 32:
369     {
370       unsigned char* p = &map[position];
371       pix = &surface[0] + ( ( height - 1 ) * width );
372
373       for( unsigned int i = 0; i < height; i++ )
374       {
375         for( unsigned int j = 0; j < width; j++ )
376         {
377           *pix++ = ARGB_JOIN( p[3], p[0], p[1], p[2] );
378           p += 4;
379         }
380         // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
381         pix -= ( width * 2 );
382       }
383       break;
384     }
385
386     case 24:
387     {
388       for( unsigned int i = 0; i < height; i++ )
389       {
390         pix = &surface[0] + ( ( height - 1 - i ) * width );
391         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
392         {
393           return false;
394         }
395         unsigned char* p = &pixbuf[0];
396         for( unsigned int j = 0; j < width; j++ )
397         {
398           *pix++ = ARGB_JOIN( 0xff, p[0], p[1], p[2] );
399           p += 3;
400         }
401       }
402       break;
403     }
404
405     case 8:
406     {
407       for( unsigned int i = 0; i < height; i++ )
408       {
409         pix = &surface[0] + ( ( height - 1 - i ) * width );
410         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
411         {
412           return false;
413         }
414         unsigned char* p = &pixbuf[0];
415         for( unsigned int j = 0; j < width; j++ )
416         {
417           *pix++ = palette[*p++];
418         }
419       }
420       break;
421     }
422
423     case 4:
424     {
425       for( unsigned int i = 0; i < height; i++ )
426       {
427         pix = &surface[0] + ( ( height - 1 - i ) * width );
428         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
429         {
430           return false;
431         }
432         unsigned char* p = &pixbuf[0];
433         for( unsigned int j = 0; j < width; j++ )
434         {
435           if( j & 0x1 )
436           {
437             *pix = palette[*p & 0x0f];
438             p++;
439           }
440           else
441           {
442             *pix = palette[*p >> 4];
443           }
444           pix++;
445         }
446       }
447       break;
448     }
449
450     case 1:
451     {
452       for( unsigned int i = 0; i < height; i++ )
453       {
454         pix = &surface[0] + ( ( height - 1 - i ) * width );
455         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
456         {
457           return false;
458         }
459         unsigned char* p = &pixbuf[0];
460
461         for( unsigned int j = 0; j < width; j += 8 )
462         {
463           *pix++ = palette[ *p >> 7 ];
464           *pix++ = palette[ *p >> 6 & 0x01 ];
465           *pix++ = palette[ *p >> 5 & 0x01 ];
466           *pix++ = palette[ *p >> 4 & 0x01 ];
467           *pix++ = palette[ *p >> 3 & 0x01 ];
468           *pix++ = palette[ *p >> 2 & 0x01 ];
469           *pix++ = palette[ *p >> 1 & 0x01 ];
470           *pix++ = palette[ *p >> 0 & 0x01 ];
471
472           p++;
473         }
474       }
475       break;
476     }
477
478     default:
479     {
480       DALI_LOG_WARNING( "Image file contains unsupported bits-per-pixel %d\n", bitcount );
481       return false;
482     }
483   }
484
485   return true;
486 }
487
488 /**
489  * @brief Apply the mask if required
490  * @param[in/out] map The map to use/set
491  * @param[in] fsize The file size
492  * @param[in/out] position The position in the file
493  * @param[in//out] maskbuf The mask buffer
494  * @param[in] bitStride The stride
495  * @param[in] width The width
496  * @param[in] height The height
497  * @param[in/out] pix A reference to the pointer to the pix buffer
498  * @param[in/out] surface A reference to the surface buffer
499  */
500 bool ApplyMask(
501     Dali::Vector<unsigned char>& map,
502     const unsigned int fsize,
503     size_t& position,
504     Dali::Vector<unsigned char>& maskbuf,
505     const unsigned int bitStride,
506     const unsigned int width,
507     const unsigned int height,
508     unsigned int*& pix,
509     Dali::Vector<unsigned int>& surface)
510 {
511   if( !read_mem( &map[0], fsize, &position, &maskbuf[0], bitStride * height ) )
512   {
513     return false;
514   }
515
516   // Apply mask.
517   // Precalc to save time in the loops.
518   unsigned int bytesPerWidth = width / 8;
519   unsigned int bytesRemainingPerWidth = width - ( bytesPerWidth << 3 );
520
521   // Loop for each line of the image.
522   for( unsigned int i = 0; i < height; ++i )
523   {
524     unsigned char *m = &maskbuf[0] + ( bitStride * i );
525     pix = &surface[0] + ( ( height - 1 - i ) * width );
526
527     // Do chunks of 8 pixels first so mask operations can be unrolled.
528     for( unsigned int j = 0; j < bytesPerWidth; ++j )
529     {
530       // Unrolled 8 bits of the mask to avoid many conditions and branches.
531       A_VAL( pix++ ) = ( *m & ( 1 << 7 ) ) ? 0x00 : 0xff;
532       A_VAL( pix++ ) = ( *m & ( 1 << 6 ) ) ? 0x00 : 0xff;
533       A_VAL( pix++ ) = ( *m & ( 1 << 5 ) ) ? 0x00 : 0xff;
534       A_VAL( pix++ ) = ( *m & ( 1 << 4 ) ) ? 0x00 : 0xff;
535       A_VAL( pix++ ) = ( *m & ( 1 << 3 ) ) ? 0x00 : 0xff;
536       A_VAL( pix++ ) = ( *m & ( 1 << 2 ) ) ? 0x00 : 0xff;
537       A_VAL( pix++ ) = ( *m & ( 1 << 1 ) ) ? 0x00 : 0xff;
538       A_VAL( pix++ ) = ( *m & ( 1 << 0 ) ) ? 0x00 : 0xff;
539       m++;
540     }
541
542     // Handle any remaining width ( < 8 ) or images that are < 8 wide.
543     if( bytesRemainingPerWidth > 0 )
544     {
545       for( unsigned int j = 0; j < bytesRemainingPerWidth; ++j )
546       {
547         // Note: Although we are doing less that a bytes worth of mask, we still always start on the first bit.
548         // If the image is smaller than 8 pixels wide, each mask will still start on a new byte.
549         A_VAL( pix++ ) = ( *m & ( 1 << ( 7 - j ) ) ) ? 0x00 : 0xff;
550       }
551       m++;
552     }
553   }
554
555   return true;
556 }
557
558 }//unnamed namespace
559
560 bool LoadIcoHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
561 {
562   IcoData chosen;
563   Dali::Vector<unsigned char> map;
564   unsigned int fsize;
565   FILE* const fp = input.file;
566
567   if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
568   {
569     return false;
570   }
571
572   width = chosen.w;
573   height = chosen.h;
574
575   return true;
576 }
577
578 bool LoadBitmapFromIco( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
579 {
580   IcoData chosen;
581   Dali::Vector<unsigned char> map;
582   unsigned int fsize;
583   FILE* const fp = input.file;
584
585   if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
586   {
587     return false;
588   }
589
590   Dali::Vector<unsigned int> pal;
591   Dali::Vector<unsigned int> surface;
592   Dali::Vector<unsigned char> maskbuf;
593   Dali::Vector<unsigned char> pixbuf;
594   pal.Resize(256 * 4);
595
596   unsigned int dword;
597   unsigned short word;
598
599   int diff_size = 0;
600   unsigned int* pix;
601
602   size_t position = chosen.bmoffset;//22 == position
603
604   unsigned int w = chosen.w;
605   unsigned int h = chosen.h;
606   unsigned int cols = chosen.cols;
607
608   // read bmp header time... let's do some checking
609   if (!read_uint(&map[0], fsize, &position, &dword))
610   {
611     return false; // headersize - dont care
612   }
613   if (!read_uint(&map[0], fsize, &position, &dword))
614   {
615     return false; // width
616   }
617   if (dword > 0)
618   {
619     if (dword != w)
620     {
621       w = dword;
622       diff_size = 1;
623     }
624   }
625   if (!read_uint(&map[0], fsize, &position, &dword))
626   {
627     return false; // height
628   }
629   if (dword > 0)
630   {
631     if (dword != (h * 2))
632     {
633       h = dword / 2;
634       diff_size = 1;
635     }
636   }
637   if (diff_size)
638   {
639     DALI_LOG_WARNING("Broken ICO file!\n");
640   }
641
642   // Set up the surface as soon as we have the width and height, so we have a black image if there are any further errors.
643   surface.Resize( w * h * 4 );
644   memset( &surface[0], 0, w * h * 4 );
645
646   if (!read_ushort(&map[0], fsize, &position, &word))
647   {
648     return false; // planes
649   }
650   //planes2 = word;
651   if (!read_ushort(&map[0], fsize, &position, &word))
652   {
653     return false; // bitcount
654   }
655   unsigned int bitcount = word;
656   if (!read_uint(&map[0], fsize, &position, &dword))
657   {
658     return false; // compression
659   }
660   //compression = dword;
661   if (!read_uint(&map[0], fsize, &position, &dword))
662   {
663     return false; // imagesize
664   }
665   //imagesize = dword;
666   if (!read_uint(&map[0], fsize, &position, &dword))
667   {
668     return false; // z pixels per m
669   }
670   if (!read_uint(&map[0], fsize, &position, &dword))
671   {
672     return false; // y pizels per m
673   }
674   if (!read_uint(&map[0], fsize, &position, &dword))
675   {
676     return false; // colors used
677   }
678   //colorsused = dword;
679   if (!read_uint(&map[0], fsize, &position, &dword))
680   {
681     return false; // colors important
682   }
683
684   for( unsigned int i = 0; i < cols ; i ++ )
685   {
686     unsigned char a, r, g, b;
687
688     if (!read_uchar(&map[0], fsize, &position, &b))
689     {
690       return false;
691     }
692     if (!read_uchar(&map[0], fsize, &position, &g))
693     {
694       return false;
695     }
696     if (!read_uchar(&map[0], fsize, &position, &r))
697     {
698       return false;
699     }
700     if (!read_uchar(&map[0], fsize, &position, &a))
701     {
702       return false;
703     }
704     pal[i] = ARGB_JOIN( 0xff, b, g, r );
705   }
706
707   // This is the reference way of calculating the total number of bytes necessary to store one row of pixels.
708   unsigned int stride = ( ( ( bitcount * w ) + 31 ) / 32 ) * 4;
709   unsigned int bitStride = ( ( w + 31 ) / 32 ) * 4;
710
711   // Pixbuf only ever contains one scanline worth of data.
712   pixbuf.Resize( stride );
713   maskbuf.Resize( bitStride * h );
714
715   // Handle different bits-per-pixel.
716   if(!HandleBitsPerPixel(bitcount, map, pix, surface, w, h, fsize, position, pixbuf, stride, pal))
717   {
718     return false;
719   }
720
721   // From the spec: If bpp is less than 32, there will be a 1bpp mask bitmap also.
722   if(( bitcount < 32 ) && !ApplyMask(map, fsize, position, maskbuf, bitStride, w, h, pix, surface))
723   {
724     // Return false if not able to apply mask when the bpp is less than 32
725     return false;
726   }
727
728   bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
729   auto pixels = bitmap.GetBuffer();
730   memcpy( pixels, &surface[0], w * h * 4 );
731
732   return true;
733 }
734
735 }
736
737 }