Dali 3D User Interface Engine
loader-ico.cpp
Go to the documentation of this file.
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>
56 
57 // INTERNAL INCLUDES
60 
61 namespace Dali
62 {
63 using Integration::Bitmap;
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 {
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,
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!");
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;
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 
364 {
365  IcoData chosen;
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 
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!");
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 }
Dali Docs Home
Read more about Dali