2 * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Derived from Enlightenment file evas_image_load_ico.c[1] which is licensed
19 * under the BSD 2-clause license[2] reproduced below.
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/]
24 * Copyright (C) 2002-2012 Carsten Haitzler, Dan Sinclair, Mike Blumenkrantz,
25 * Samsung Electronics and various contributors (see AUTHORS)
27 * All rights reserved.
29 * Redistribution and use in source and binary forms, with or without
30 * modification, are permitted provided that the following conditions are met:
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.
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.
51 #include <dali/internal/imaging/common/loader-ico.h>
55 #include <dali/public-api/common/dali-vector.h>
58 #include <dali/integration-api/debug.h>
59 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
64 namespace TizenPlatform
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;
76 typedef unsigned char DATA8;
77 #define A_VAL(p) (reinterpret_cast< DATA8 * >( p )[3])
79 #define RGB_JOIN(r,g,b) \
80 (((r) << 16) + ((g) << 8) + (b))
82 #define ARGB_JOIN(a,r,g,b) \
83 (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
85 bool read_ushort(unsigned char *map, size_t length, size_t *position, unsigned short *ret)
89 if (*position + 2 > length)
93 b[0] = map[(*position)++];
94 b[1] = map[(*position)++];
95 *ret = (b[1] << 8) | b[0];
99 bool read_uint(unsigned char *map, size_t length, size_t *position, unsigned int *ret)
104 if (*position + 4 > length)
108 for (i = 0; i < 4; i++)
110 b[i] = map[(*position)++];
112 *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]);
116 bool read_uchar(unsigned char *map, size_t length, size_t *position, unsigned char *ret)
118 if (*position + 1 > length)
122 *ret = map[(*position)++];
126 bool read_mem(unsigned char *map, size_t length, size_t *position, void *buffer, int size)
128 if (*position + size > length)
132 memcpy(buffer, map + *position, size);
158 unsigned int bmoffset, bmsize;
161 bool LoadIcoHeaderHelper( FILE* fp,
163 Dali::Vector<unsigned char>& map,
164 unsigned int& fsize )
166 memset( &chosen, 0, sizeof(chosen) );
170 DALI_LOG_ERROR("Error loading bitmap\n");
177 if( fseek(fp,0,SEEK_END) )
179 DALI_LOG_ERROR("Error seeking ICO data\n");
183 long positionIndicator = ftell(fp);
186 if( positionIndicator > -1L )
188 fsize = static_cast<unsigned int>(positionIndicator);
196 if( fseek(fp, 0, SEEK_SET) )
198 DALI_LOG_ERROR("Error seeking ICO data\n");
202 if (fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
208 if(fread(&map[0], 1, fsize, fp) != fsize)
210 DALI_LOG_WARNING("image file read opeation error!\n");
214 int search = BIGGEST;
215 unsigned short reserved, type, count;
216 if (!read_ushort(&map[0], fsize, &position, &reserved))
220 if (!read_ushort(&map[0], fsize, &position, &type))
224 if (!read_ushort(&map[0], fsize, &position, &count))
228 if (!((reserved == 0) &&
229 ((type == ICON) || (type == CURSOR)) && (count != 0)))
235 bool have_choice = false;
237 for (unsigned short i = 0; i < count; i++)
239 unsigned char tw = 0, th = 0, tcols = 0;
240 if (!read_uchar(&map[0], fsize, &position, &tw))
249 if (!read_uchar(&map[0], fsize, &position, &th))
259 if (!read_uchar(&map[0], fsize, &position, &tcols))
264 if (!read_uchar(&map[0], fsize, &position, &byte))
268 if (!read_ushort(&map[0], fsize, &position, &word))
278 if (!read_ushort(&map[0], fsize, &position, &word))
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 )
296 unsigned int bmoffset, bmsize;
297 if (!read_uint(&map[0], fsize, &position, &bmsize))
301 if (!read_uint(&map[0], fsize, &position, &bmoffset))
305 if ((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
309 if (search == BIGGEST)
312 if ((!have_choice) ||
313 ((pdelta >= chosen.pdelta) &&
314 (((bpp >= 3) && (bpp >= chosen.bpp)) ||
315 ((bpp < 3) && (cols >= chosen.cols)))))
318 chosen.pdelta = pdelta;
323 chosen.planes = planes;
324 chosen.bmsize = bmsize;
325 chosen.bmoffset = bmoffset;
330 if (chosen.bmoffset == 0)
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
352 bool HandleBitsPerPixel(
353 const unsigned int bitcount,
354 Dali::Vector<unsigned char>& map,
356 Dali::Vector<unsigned int>& surface,
357 const unsigned int width,
358 const unsigned int height,
359 const unsigned int fsize,
361 Dali::Vector<unsigned char>& pixbuf,
362 const unsigned int stride,
363 const Dali::Vector<unsigned int>& palette)
365 // Note: Switch is in order of most common format first.
370 unsigned char* p = &map[position];
371 pix = &surface[0] + ( ( height - 1 ) * width );
373 for( unsigned int i = 0; i < height; i++ )
375 for( unsigned int j = 0; j < width; j++ )
377 *pix++ = ARGB_JOIN( p[3], p[0], p[1], p[2] );
380 // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
381 pix -= ( width * 2 );
388 for( unsigned int i = 0; i < height; i++ )
390 pix = &surface[0] + ( ( height - 1 - i ) * width );
391 if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
395 unsigned char* p = &pixbuf[0];
396 for( unsigned int j = 0; j < width; j++ )
398 *pix++ = ARGB_JOIN( 0xff, p[0], p[1], p[2] );
407 for( unsigned int i = 0; i < height; i++ )
409 pix = &surface[0] + ( ( height - 1 - i ) * width );
410 if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
414 unsigned char* p = &pixbuf[0];
415 for( unsigned int j = 0; j < width; j++ )
417 *pix++ = palette[*p++];
425 for( unsigned int i = 0; i < height; i++ )
427 pix = &surface[0] + ( ( height - 1 - i ) * width );
428 if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
432 unsigned char* p = &pixbuf[0];
433 for( unsigned int j = 0; j < width; j++ )
437 *pix = palette[*p & 0x0f];
442 *pix = palette[*p >> 4];
452 for( unsigned int i = 0; i < height; i++ )
454 pix = &surface[0] + ( ( height - 1 - i ) * width );
455 if( !read_mem( &map[0], fsize, &position, &pixbuf[0], stride ) )
459 unsigned char* p = &pixbuf[0];
461 for( unsigned int j = 0; j < width; j += 8 )
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 ];
480 DALI_LOG_WARNING( "Image file contains unsupported bits-per-pixel %d\n", bitcount );
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
501 Dali::Vector<unsigned char>& map,
502 const unsigned int fsize,
504 Dali::Vector<unsigned char>& maskbuf,
505 const unsigned int bitStride,
506 const unsigned int width,
507 const unsigned int height,
509 Dali::Vector<unsigned int>& surface)
511 if( !read_mem( &map[0], fsize, &position, &maskbuf[0], bitStride * height ) )
517 // Precalc to save time in the loops.
518 unsigned int bytesPerWidth = width / 8;
519 unsigned int bytesRemainingPerWidth = width - ( bytesPerWidth << 3 );
521 // Loop for each line of the image.
522 for( unsigned int i = 0; i < height; ++i )
524 unsigned char *m = &maskbuf[0] + ( bitStride * i );
525 pix = &surface[0] + ( ( height - 1 - i ) * width );
527 // Do chunks of 8 pixels first so mask operations can be unrolled.
528 for( unsigned int j = 0; j < bytesPerWidth; ++j )
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;
542 // Handle any remaining width ( < 8 ) or images that are < 8 wide.
543 if( bytesRemainingPerWidth > 0 )
545 for( unsigned int j = 0; j < bytesRemainingPerWidth; ++j )
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;
560 bool LoadIcoHeader( const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height )
563 Dali::Vector<unsigned char> map;
565 FILE* const fp = input.file;
567 if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
578 bool LoadBitmapFromIco( const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
581 Dali::Vector<unsigned char> map;
583 FILE* const fp = input.file;
585 if ( false == LoadIcoHeaderHelper(fp, chosen, map, fsize) )
590 Dali::Vector<unsigned int> pal;
591 Dali::Vector<unsigned int> surface;
592 Dali::Vector<unsigned char> maskbuf;
593 Dali::Vector<unsigned char> pixbuf;
602 size_t position = chosen.bmoffset;//22 == position
604 unsigned int w = chosen.w;
605 unsigned int h = chosen.h;
606 unsigned int cols = chosen.cols;
608 // read bmp header time... let's do some checking
609 if (!read_uint(&map[0], fsize, &position, &dword))
611 return false; // headersize - dont care
613 if (!read_uint(&map[0], fsize, &position, &dword))
615 return false; // width
625 if (!read_uint(&map[0], fsize, &position, &dword))
627 return false; // height
631 if (dword != (h * 2))
639 DALI_LOG_WARNING("Broken ICO file!\n");
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 );
646 if (!read_ushort(&map[0], fsize, &position, &word))
648 return false; // planes
651 if (!read_ushort(&map[0], fsize, &position, &word))
653 return false; // bitcount
655 unsigned int bitcount = word;
656 if (!read_uint(&map[0], fsize, &position, &dword))
658 return false; // compression
660 //compression = dword;
661 if (!read_uint(&map[0], fsize, &position, &dword))
663 return false; // imagesize
666 if (!read_uint(&map[0], fsize, &position, &dword))
668 return false; // z pixels per m
670 if (!read_uint(&map[0], fsize, &position, &dword))
672 return false; // y pizels per m
674 if (!read_uint(&map[0], fsize, &position, &dword))
676 return false; // colors used
678 //colorsused = dword;
679 if (!read_uint(&map[0], fsize, &position, &dword))
681 return false; // colors important
684 for( unsigned int i = 0; i < cols ; i ++ )
686 unsigned char a, r, g, b;
688 if (!read_uchar(&map[0], fsize, &position, &b))
692 if (!read_uchar(&map[0], fsize, &position, &g))
696 if (!read_uchar(&map[0], fsize, &position, &r))
700 if (!read_uchar(&map[0], fsize, &position, &a))
704 pal[i] = ARGB_JOIN( 0xff, b, g, r );
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;
711 // Pixbuf only ever contains one scanline worth of data.
712 pixbuf.Resize( stride );
713 maskbuf.Resize( bitStride * h );
715 // Handle different bits-per-pixel.
716 if(!HandleBitsPerPixel(bitcount, map, pix, surface, w, h, fsize, position, pixbuf, stride, pal))
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))
724 // Return false if not able to apply mask when the bpp is less than 32
728 bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
729 auto pixels = bitmap.GetBuffer();
730 memcpy( pixels, &surface[0], w * h * 4 );