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