acb74f42bc11863666b3d6d71e5198d6a13cd72c
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-ico.cpp
1 /*
2  * Copyright (c) 2017 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 #include <dali/internal/system/common/file-closer.h>
62
63 using namespace Dali::Internal::Platform;
64
65 namespace Dali
66 {
67
68 namespace TizenPlatform
69 {
70
71 namespace
72 {
73 // Reserved 2 bytes + type 2 bytes + count 2 bytes + count * 16 bytes
74 const unsigned char ICO_FILE_HEADER = 22;
75 // Info header 40 bytes = size 4 bytes + width 4 bytes + height 4 bytes + planes 2 bytes + bitcount 2 bytes
76 // + compression 4 bytes + imagesize 4 bytes + xpixelsPerM 4 bytes + ypixelsPerM 4 bytes + colorsUsed 4 bytes + colorImportant 4 bytes
77 // besides, there are rgba color data = numberOfColors * 4 bytes
78 const unsigned char ICO_IMAGE_INFO_HEADER = 40;
79
80 typedef unsigned char  DATA8;
81 #define A_VAL(p) (reinterpret_cast< DATA8 * >( p )[3])
82
83 #define RGB_JOIN(r,g,b) \
84                 (((r) << 16) + ((g) << 8) + (b))
85
86 #define ARGB_JOIN(a,r,g,b) \
87                 (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
88
89 bool read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret)
90 {
91   unsigned char b[2];
92
93   if (*position + 2 > length)
94   {
95     return false;
96   }
97   b[0] = map[(*position)++];
98   b[1] = map[(*position)++];
99   *ret = (b[1] << 8) | b[0];
100   return true;
101 }
102
103 bool read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret)
104 {
105   unsigned char b[4];
106   unsigned int i;
107
108   if (*position + 4 > length)
109   {
110     return false;
111   }
112   for (i = 0; i < 4; i++)
113   {
114     b[i] = map[(*position)++];
115   }
116   *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]);
117   return true;
118 }
119
120 bool read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret)
121 {
122   if (*position + 1 > length)
123   {
124     return false;
125   }
126   *ret = map[(*position)++];
127   return true;
128 }
129
130 bool read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size)
131 {
132   if (*position + size > length)
133   {
134     return false;
135   }
136   memcpy(buffer, map + *position, size);
137   *position += size;
138   return true;
139 }
140
141 enum
142 {
143   SMALLEST,
144   BIGGEST,
145   SMALLER,
146   BIGGER
147 };
148
149 enum
150 {
151   ICON = 1,
152   CURSOR = 2
153 };
154
155 struct IcoData
156 {
157   int pdelta;
158   int w, h;
159   int cols;
160   int bpp, planes;
161   int hot_x, hot_y;
162   unsigned int bmoffset, bmsize;
163 };
164
165 bool LoadIcoHeaderHelper( FILE* fp,
166                           IcoData& chosen,
167                           Dali::Vector<unsigned char>& map,
168                           unsigned int& fsize )
169 {
170   memset( &chosen, 0, sizeof(chosen) );
171
172   if(fp == NULL)
173   {
174     DALI_LOG_ERROR("Error loading bitmap\n");
175     return false;
176   }
177   size_t position = 0;
178   unsigned short word;
179   unsigned char byte;
180
181   if( InternalFile::fseek(fp,0,SEEK_END) )
182   {
183     DALI_LOG_ERROR("Error seeking ICO data\n");
184     return false;
185   }
186
187   long positionIndicator = InternalFile::ftell(fp);
188   fsize = 0u;
189
190   if( positionIndicator > -1L )
191   {
192     fsize = static_cast<unsigned int>(positionIndicator);
193   }
194
195   if( 0u == fsize )
196   {
197     return false;
198   }
199
200   if( InternalFile::fseek(fp, 0, SEEK_SET) )
201   {
202     DALI_LOG_ERROR("Error seeking ICO data\n");
203     return false;
204   }
205
206   if (fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
207   {
208     return false;
209   }
210   map.Resize(fsize);
211
212   if(InternalFile::fread(&map[0], 1, fsize, fp) != fsize)
213   {
214     DALI_LOG_WARNING("image file read opeation error!\n");
215     return false;
216   }
217
218   int search = BIGGEST;
219   unsigned short reserved, type, count;
220   if (!read_ushort(&map[0], fsize, &position, &reserved))
221   {
222     return false;
223   }
224   if (!read_ushort(&map[0], fsize, &position, &type))
225   {
226     return false;
227   }
228   if (!read_ushort(&map[0], fsize, &position, &count))
229   {
230     return false;
231   }
232   if (!((reserved == 0) &&
233        ((type == ICON) || (type == CURSOR)) && (count != 0)))
234   {
235     return false;
236   }
237   search = BIGGEST;
238   chosen.pdelta = 0;
239   bool have_choice = false;
240
241   for (unsigned short i = 0; i < count; i++)
242   {
243     unsigned char tw = 0, th = 0, tcols = 0;
244     if (!read_uchar(&map[0], fsize, &position, &tw))
245     {
246       return false;
247     }
248     int w = tw;
249     if (w <= 0)
250     {
251       w = 256;
252     }
253     if (!read_uchar(&map[0], fsize, &position, &th))
254     {
255       return false;
256
257     }
258     int h = th;
259     if (h <= 0)
260     {
261       h = 256;
262     }
263     if (!read_uchar(&map[0], fsize, &position, &tcols))
264     {
265       return false;
266     }
267     int cols = tcols;
268     if (!read_uchar(&map[0], fsize, &position, &byte))
269     {
270       return false;
271     }
272     if (!read_ushort(&map[0], fsize, &position, &word))
273     {
274       return false;
275     }
276     int planes=0;
277     if (type == 1)
278     {
279       planes = word;
280     }
281     //else hot_x = word;
282     if (!read_ushort(&map[0], fsize, &position, &word))
283     {
284       return false;
285     }
286     int bpp=0;
287     if (type == 1)
288     {
289       bpp = word;
290     }
291
292     // 0 colors means 256 for paletized modes.
293     // Note: We must not do this conversion for bpp greater than 8, as there is no palette.
294     if( bpp <= 8 && cols == 0 )
295     {
296       cols = 256;
297     }
298
299     //else hot_y = word;
300     unsigned int bmoffset, bmsize;
301     if (!read_uint(&map[0], fsize, &position, &bmsize))
302     {
303       return false;
304     }
305     if (!read_uint(&map[0], fsize, &position, &bmoffset))
306     {
307       return false;
308     }
309     if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
310     {
311       return false;
312     }
313     if (search == BIGGEST)
314     {
315       int pdelta = w * h;
316       if ((!have_choice) ||
317        ((pdelta >= chosen.pdelta) &&
318            (((bpp >= 3) && (bpp >= chosen.bpp)) ||
319                ((bpp < 3) && (cols >= chosen.cols)))))
320       {
321         have_choice = true;
322         chosen.pdelta = pdelta;
323         chosen.w = w;
324         chosen.h = h;
325         chosen.cols = cols;
326         chosen.bpp = bpp;
327         chosen.planes = planes;
328         chosen.bmsize = bmsize;
329         chosen.bmoffset = bmoffset;
330       }
331     }
332   }
333
334   if (chosen.bmoffset == 0)
335   {
336     return false;
337   }
338
339   return true;
340 }
341
342 }//unnamed namespace
343
344 bool LoadIcoHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
345 {
346   IcoData chosen;
347   Dali::Vector<unsigned char> map;
348   unsigned int fsize;
349   FILE* const fp = input.file;
350
351   if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
352   {
353     return false;
354   }
355
356   width = chosen.w;
357   height = chosen.h;
358
359   return true;
360 }
361
362 bool LoadBitmapFromIco( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
363 {
364   IcoData chosen;
365   Dali::Vector<unsigned char> map;
366   unsigned int fsize;
367   FILE* const fp = input.file;
368
369   if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
370   {
371     return false;
372   }
373
374   Dali::Vector<unsigned int> pal;
375   Dali::Vector<unsigned int> surface;
376   Dali::Vector<unsigned char> maskbuf;
377   Dali::Vector<unsigned char> pixbuf;
378   pal.Resize(256 * 4);
379
380   unsigned int dword;
381   unsigned short word;
382
383   int diff_size = 0;
384   unsigned int* pix;
385
386   size_t position = chosen.bmoffset;//22 == position
387
388   unsigned int w = chosen.w;
389   unsigned int h = chosen.h;
390   unsigned int cols = chosen.cols;
391
392   // read bmp header time... let's do some checking
393   if (!read_uint(&map[0], fsize, &position, &dword))
394   {
395     return false; // headersize - dont care
396   }
397   if (!read_uint(&map[0], fsize, &position, &dword))
398   {
399     return false; // width
400   }
401   if (dword > 0)
402   {
403     if (dword != w)
404     {
405       w = dword;
406       diff_size = 1;
407     }
408   }
409   if (!read_uint(&map[0], fsize, &position, &dword))
410   {
411     return false; // height
412   }
413   if (dword > 0)
414   {
415     if (dword != (h * 2))
416     {
417       h = dword / 2;
418       diff_size = 1;
419     }
420   }
421   if (diff_size)
422   {
423     DALI_LOG_WARNING("Broken ICO file!\n");
424   }
425
426   // 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.
427   surface.Resize( w * h * 4 );
428   memset( &surface[0], 0, w * h * 4 );
429
430   if (!read_ushort(&map[0], fsize, &position, &word))
431   {
432     return false; // planes
433   }
434   //planes2 = word;
435   if (!read_ushort(&map[0], fsize, &position, &word))
436   {
437     return false; // bitcount
438   }
439   unsigned int bitcount = word;
440   if (!read_uint(&map[0], fsize, &position, &dword))
441   {
442     return false; // compression
443   }
444   //compression = dword;
445   if (!read_uint(&map[0], fsize, &position, &dword))
446   {
447     return false; // imagesize
448   }
449   //imagesize = dword;
450   if (!read_uint(&map[0], fsize, &position, &dword))
451   {
452     return false; // z pixels per m
453   }
454   if (!read_uint(&map[0], fsize, &position, &dword))
455   {
456     return false; // y pizels per m
457   }
458   if (!read_uint(&map[0], fsize, &position, &dword))
459   {
460     return false; // colors used
461   }
462   //colorsused = dword;
463   if (!read_uint(&map[0], fsize, &position, &dword))
464   {
465     return false; // colors important
466   }
467
468   for( unsigned int i = 0; i < cols ; i ++ )
469   {
470     unsigned char a, r, g, b;
471
472     if (!read_uchar(&map[0], fsize, &position, &b))
473     {
474       return false;
475     }
476     if (!read_uchar(&map[0], fsize, &position, &g))
477     {
478       return false;
479     }
480     if (!read_uchar(&map[0], fsize, &position, &r))
481     {
482       return false;
483     }
484     if (!read_uchar(&map[0], fsize, &position, &a))
485     {
486       return false;
487     }
488     pal[i] = ARGB_JOIN( 0xff, b, g, r );
489   }
490
491   // This is the reference way of calculating the total number of bytes necessary to store one row of pixels.
492   unsigned int stride = ( ( ( bitcount * w ) + 31 ) / 32 ) * 4;
493   unsigned int bitStride = ( ( w + 31 ) / 32 ) * 4;
494
495   // Pixbuf only ever contains one scanline worth of data.
496   pixbuf.Resize( stride );
497   maskbuf.Resize( bitStride * h );
498
499   // Handle different bits-per-pixel.
500   // Note: Switch is in order of most common format first.
501   switch( bitcount )
502   {
503     case 32:
504     {
505       unsigned char* p = &map[position];
506       pix = &surface[0] + ( ( h - 1 ) * w );
507
508       for( unsigned int i = 0; i < h; i++ )
509       {
510         for( unsigned int j = 0; j < w; j++ )
511         {
512           *pix++ = ARGB_JOIN( p[3], p[0], p[1], p[2] );
513           p += 4;
514         }
515         // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
516         pix -= ( w * 2 );
517       }
518       break;
519     }
520
521     case 24:
522     {
523       for( unsigned int i = 0; i < h; i++ )
524       {
525         pix = &surface[0] + ( ( h - 1 - i ) * w );
526         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
527         {
528           return false;
529         }
530         unsigned char* p = &pixbuf[0];
531         for( unsigned int j = 0; j < w; j++ )
532         {
533           *pix++ = ARGB_JOIN( 0xff, p[0], p[1], p[2] );
534           p += 3;
535         }
536       }
537       break;
538     }
539
540     case 8:
541     {
542       for( unsigned int i = 0; i < h; i++ )
543       {
544         pix = &surface[0] + ( ( h - 1 - i ) * w );
545         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
546         {
547           return false;
548         }
549         unsigned char* p = &pixbuf[0];
550         for( unsigned int j = 0; j < w; j++ )
551         {
552           *pix++ = pal[*p++];
553         }
554       }
555       break;
556     }
557
558     case 4:
559     {
560       for( unsigned int i = 0; i < h; i++ )
561       {
562         pix = &surface[0] + ( ( h - 1 - i ) * w );
563         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
564         {
565           return false;
566         }
567         unsigned char* p = &pixbuf[0];
568         for( unsigned int j = 0; j < w; j++ )
569         {
570           if( j & 0x1 )
571           {
572             *pix = pal[*p & 0x0f];
573             p++;
574           }
575           else
576           {
577             *pix = pal[*p >> 4];
578           }
579           pix++;
580         }
581       }
582       break;
583     }
584
585     case 1:
586     {
587       for( unsigned int i = 0; i < h; i++ )
588       {
589         pix = &surface[0] + ( ( h - 1 - i ) * w );
590         if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
591         {
592           return false;
593         }
594         unsigned char* p = &pixbuf[0];
595
596         for( unsigned int j = 0; j < w; j += 8 )
597         {
598           *pix++ = pal[ *p >> 7 ];
599           *pix++ = pal[ *p >> 6 & 0x01 ];
600           *pix++ = pal[ *p >> 5 & 0x01 ];
601           *pix++ = pal[ *p >> 4 & 0x01 ];
602           *pix++ = pal[ *p >> 3 & 0x01 ];
603           *pix++ = pal[ *p >> 2 & 0x01 ];
604           *pix++ = pal[ *p >> 1 & 0x01 ];
605           *pix++ = pal[ *p >> 0 & 0x01 ];
606
607           p++;
608         }
609       }
610       break;
611     }
612
613     default:
614     {
615       DALI_LOG_WARNING( "Image file contains unsupported bits-per-pixel %d\n", bitcount );
616       return false;
617     }
618   }
619
620   // From the spec: If bpp is less than 32, there will be a 1bpp mask bitmap also.
621   if( bitcount < 32 )
622   {
623     if( !read_mem( &map[0], fsize, &position, &maskbuf[0], bitStride * h ) )
624     {
625       return false;
626     }
627
628     // Apply mask.
629     // Precalc to save time in the loops.
630     unsigned int bytesPerWidth = w / 8;
631     unsigned int bytesRemainingPerWidth = w - ( bytesPerWidth << 3 );
632
633     // Loop for each line of the image.
634     for( unsigned int i = 0; i < h; ++i )
635     {
636       unsigned char *m = &maskbuf[0] + ( bitStride * i );
637       pix = &surface[0] + ( ( h - 1 - i ) * w );
638
639       // Do chunks of 8 pixels first so mask operations can be unrolled.
640       for( unsigned int j = 0; j < bytesPerWidth; ++j )
641       {
642         // Unrolled 8 bits of the mask to avoid many conditions and branches.
643         A_VAL( pix++ ) = ( *m & ( 1 << 7 ) ) ? 0x00 : 0xff;
644         A_VAL( pix++ ) = ( *m & ( 1 << 6 ) ) ? 0x00 : 0xff;
645         A_VAL( pix++ ) = ( *m & ( 1 << 5 ) ) ? 0x00 : 0xff;
646         A_VAL( pix++ ) = ( *m & ( 1 << 4 ) ) ? 0x00 : 0xff;
647         A_VAL( pix++ ) = ( *m & ( 1 << 3 ) ) ? 0x00 : 0xff;
648         A_VAL( pix++ ) = ( *m & ( 1 << 2 ) ) ? 0x00 : 0xff;
649         A_VAL( pix++ ) = ( *m & ( 1 << 1 ) ) ? 0x00 : 0xff;
650         A_VAL( pix++ ) = ( *m & ( 1 << 0 ) ) ? 0x00 : 0xff;
651         m++;
652       }
653
654       // Handle any remaining width ( < 8 ) or images that are < 8 wide.
655       if( bytesRemainingPerWidth > 0 )
656       {
657         for( unsigned int j = 0; j < bytesRemainingPerWidth; ++j )
658         {
659           // Note: Although we are doing less that a bytes worth of mask, we still always start on the first bit.
660           // If the image is smaller than 8 pixels wide, each mask will still start on a new byte.
661           A_VAL( pix++ ) = ( *m & ( 1 << ( 7 - j ) ) ) ? 0x00 : 0xff;
662         }
663         m++;
664       }
665     }
666   }
667
668   bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
669   auto pixels = bitmap.GetBuffer();
670   memcpy( pixels, &surface[0], w * h * 4 );
671
672   return true;
673 }
674
675 }
676
677 }