2 * Copyright (c) 2021 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>
54 #include <dali/public-api/common/dali-vector.h>
58 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
59 #include <dali/integration-api/debug.h>
63 namespace TizenPlatform
67 // Reserved 2 bytes + type 2 bytes + count 2 bytes + count * 16 bytes
68 const unsigned char ICO_FILE_HEADER = 22;
69 // Info header 40 bytes = size 4 bytes + width 4 bytes + height 4 bytes + planes 2 bytes + bitcount 2 bytes
70 // + compression 4 bytes + imagesize 4 bytes + xpixelsPerM 4 bytes + ypixelsPerM 4 bytes + colorsUsed 4 bytes + colorImportant 4 bytes
71 // besides, there are rgba color data = numberOfColors * 4 bytes
72 const unsigned char ICO_IMAGE_INFO_HEADER = 40;
74 typedef unsigned char DATA8;
75 #define A_VAL(p) (reinterpret_cast<DATA8*>(p)[3])
77 #define RGB_JOIN(r, g, b) \
78 (((r) << 16) + ((g) << 8) + (b))
80 #define ARGB_JOIN(a, r, g, b) \
81 (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
83 bool read_ushort(unsigned char* map, size_t length, size_t* position, unsigned short* ret)
87 if(*position + 2 > length)
91 b[0] = map[(*position)++];
92 b[1] = map[(*position)++];
93 *ret = (b[1] << 8) | b[0];
97 bool read_uint(unsigned char* map, size_t length, size_t* position, unsigned int* ret)
102 if(*position + 4 > length)
106 for(i = 0; i < 4; i++)
108 b[i] = map[(*position)++];
110 *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]);
114 bool read_uchar(unsigned char* map, size_t length, size_t* position, unsigned char* ret)
116 if(*position + 1 > length)
120 *ret = map[(*position)++];
124 bool read_mem(unsigned char* map, size_t length, size_t* position, void* buffer, int size)
126 if(*position + size > length)
130 memcpy(buffer, map + *position, size);
156 unsigned int bmoffset, bmsize;
159 bool LoadIcoHeaderHelper(FILE* fp,
161 Dali::Vector<unsigned char>& map,
164 memset(&chosen, 0, sizeof(chosen));
168 DALI_LOG_ERROR("Error loading bitmap\n");
175 if(fseek(fp, 0, SEEK_END))
177 DALI_LOG_ERROR("Error seeking ICO data\n");
181 long positionIndicator = ftell(fp);
184 if(positionIndicator > -1L)
186 fsize = static_cast<unsigned int>(positionIndicator);
194 if(fseek(fp, 0, SEEK_SET))
196 DALI_LOG_ERROR("Error seeking ICO data\n");
200 if(fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
206 if(fread(&map[0], 1, fsize, fp) != fsize)
208 DALI_LOG_WARNING("image file read opeation error!\n");
212 int search = BIGGEST;
213 unsigned short reserved, type, count;
214 if(!read_ushort(&map[0], fsize, &position, &reserved))
218 if(!read_ushort(&map[0], fsize, &position, &type))
222 if(!read_ushort(&map[0], fsize, &position, &count))
226 if(!((reserved == 0) &&
227 ((type == ICON) || (type == CURSOR)) && (count != 0)))
233 bool have_choice = false;
235 for(unsigned short i = 0; i < count; i++)
237 unsigned char tw = 0, th = 0, tcols = 0;
238 if(!read_uchar(&map[0], fsize, &position, &tw))
247 if(!read_uchar(&map[0], fsize, &position, &th))
256 if(!read_uchar(&map[0], fsize, &position, &tcols))
261 if(!read_uchar(&map[0], fsize, &position, &byte))
265 if(!read_ushort(&map[0], fsize, &position, &word))
275 if(!read_ushort(&map[0], fsize, &position, &word))
285 // 0 colors means 256 for paletized modes.
286 // Note: We must not do this conversion for bpp greater than 8, as there is no palette.
287 if(bpp <= 8 && cols == 0)
293 unsigned int bmoffset, bmsize;
294 if(!read_uint(&map[0], fsize, &position, &bmsize))
298 if(!read_uint(&map[0], fsize, &position, &bmoffset))
302 if((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
306 if(search == BIGGEST)
310 ((pdelta >= chosen.pdelta) &&
311 (((bpp >= 3) && (bpp >= chosen.bpp)) ||
312 ((bpp < 3) && (cols >= chosen.cols)))))
315 chosen.pdelta = pdelta;
320 chosen.planes = planes;
321 chosen.bmsize = bmsize;
322 chosen.bmoffset = bmoffset;
327 if(chosen.bmoffset == 0)
336 * @brief Handle the different bits per pixel
337 * @param[in] bitcount The bit count
338 * @param[in/out] map The map to use/set
339 * @param[in/out] pix A reference to the pointer to the pix buffer
340 * @param[in/out] surface A reference to the surface buffer
341 * @param[in] width The width
342 * @param[in] height The height
343 * @param[in] fsize The file size
344 * @param[in/out] position The position in the file
345 * @param[/outin] pixbuf A reference to the pixbuf
346 * @param[in] stride The stride to use
347 * @param[in] palette The palette
349 bool HandleBitsPerPixel(
350 const unsigned int bitcount,
351 Dali::Vector<unsigned char>& map,
353 Dali::Vector<unsigned int>& surface,
354 const unsigned int width,
355 const unsigned int height,
356 const unsigned int fsize,
358 Dali::Vector<unsigned char>& pixbuf,
359 const unsigned int stride,
360 const Dali::Vector<unsigned int>& palette)
362 // Note: Switch is in order of most common format first.
367 unsigned char* p = &map[position];
368 pix = &surface[0] + ((height - 1) * width);
370 for(unsigned int i = 0; i < height; i++)
372 for(unsigned int j = 0; j < width; j++)
374 *pix++ = ARGB_JOIN(p[3], p[0], p[1], p[2]);
377 // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
385 for(unsigned int i = 0; i < height; i++)
387 pix = &surface[0] + ((height - 1 - i) * width);
388 if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
392 unsigned char* p = &pixbuf[0];
393 for(unsigned int j = 0; j < width; j++)
395 *pix++ = ARGB_JOIN(0xff, p[0], p[1], p[2]);
404 for(unsigned int i = 0; i < height; i++)
406 pix = &surface[0] + ((height - 1 - i) * width);
407 if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
411 unsigned char* p = &pixbuf[0];
412 for(unsigned int j = 0; j < width; j++)
414 *pix++ = palette[*p++];
422 for(unsigned int i = 0; i < height; i++)
424 pix = &surface[0] + ((height - 1 - i) * width);
425 if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
429 unsigned char* p = &pixbuf[0];
430 for(unsigned int j = 0; j < width; j++)
434 *pix = palette[*p & 0x0f];
439 *pix = palette[*p >> 4];
449 for(unsigned int i = 0; i < height; i++)
451 pix = &surface[0] + ((height - 1 - i) * width);
452 if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
456 unsigned char* p = &pixbuf[0];
458 for(unsigned int j = 0; j < width; j += 8)
460 *pix++ = palette[*p >> 7];
461 *pix++ = palette[*p >> 6 & 0x01];
462 *pix++ = palette[*p >> 5 & 0x01];
463 *pix++ = palette[*p >> 4 & 0x01];
464 *pix++ = palette[*p >> 3 & 0x01];
465 *pix++ = palette[*p >> 2 & 0x01];
466 *pix++ = palette[*p >> 1 & 0x01];
467 *pix++ = palette[*p >> 0 & 0x01];
477 DALI_LOG_WARNING("Image file contains unsupported bits-per-pixel %d\n", bitcount);
486 * @brief Apply the mask if required
487 * @param[in/out] map The map to use/set
488 * @param[in] fsize The file size
489 * @param[in/out] position The position in the file
490 * @param[in//out] maskbuf The mask buffer
491 * @param[in] bitStride The stride
492 * @param[in] width The width
493 * @param[in] height The height
494 * @param[in/out] pix A reference to the pointer to the pix buffer
495 * @param[in/out] surface A reference to the surface buffer
498 Dali::Vector<unsigned char>& map,
499 const unsigned int fsize,
501 Dali::Vector<unsigned char>& maskbuf,
502 const unsigned int bitStride,
503 const unsigned int width,
504 const unsigned int height,
506 Dali::Vector<unsigned int>& surface)
508 if(!read_mem(&map[0], fsize, &position, &maskbuf[0], bitStride * height))
514 // Precalc to save time in the loops.
515 unsigned int bytesPerWidth = width / 8;
516 unsigned int bytesRemainingPerWidth = width - (bytesPerWidth << 3);
518 // Loop for each line of the image.
519 for(unsigned int i = 0; i < height; ++i)
521 unsigned char* m = &maskbuf[0] + (bitStride * i);
522 pix = &surface[0] + ((height - 1 - i) * width);
524 // Do chunks of 8 pixels first so mask operations can be unrolled.
525 for(unsigned int j = 0; j < bytesPerWidth; ++j)
527 // Unrolled 8 bits of the mask to avoid many conditions and branches.
528 A_VAL(pix++) = (*m & (1 << 7)) ? 0x00 : 0xff;
529 A_VAL(pix++) = (*m & (1 << 6)) ? 0x00 : 0xff;
530 A_VAL(pix++) = (*m & (1 << 5)) ? 0x00 : 0xff;
531 A_VAL(pix++) = (*m & (1 << 4)) ? 0x00 : 0xff;
532 A_VAL(pix++) = (*m & (1 << 3)) ? 0x00 : 0xff;
533 A_VAL(pix++) = (*m & (1 << 2)) ? 0x00 : 0xff;
534 A_VAL(pix++) = (*m & (1 << 1)) ? 0x00 : 0xff;
535 A_VAL(pix++) = (*m & (1 << 0)) ? 0x00 : 0xff;
539 // Handle any remaining width ( < 8 ) or images that are < 8 wide.
540 if(bytesRemainingPerWidth > 0)
542 for(unsigned int j = 0; j < bytesRemainingPerWidth; ++j)
544 // Note: Although we are doing less that a bytes worth of mask, we still always start on the first bit.
545 // If the image is smaller than 8 pixels wide, each mask will still start on a new byte.
546 A_VAL(pix++) = (*m & (1 << (7 - j))) ? 0x00 : 0xff;
555 } //unnamed namespace
557 bool LoadIcoHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
560 Dali::Vector<unsigned char> map;
562 FILE* const fp = input.file;
564 if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
575 bool LoadBitmapFromIco(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
578 Dali::Vector<unsigned char> map;
580 FILE* const fp = input.file;
582 if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
587 Dali::Vector<unsigned int> pal;
588 Dali::Vector<unsigned int> surface;
589 Dali::Vector<unsigned char> maskbuf;
590 Dali::Vector<unsigned char> pixbuf;
599 size_t position = chosen.bmoffset; //22 == position
601 unsigned int w = chosen.w;
602 unsigned int h = chosen.h;
603 unsigned int cols = chosen.cols;
605 // read bmp header time... let's do some checking
606 if(!read_uint(&map[0], fsize, &position, &dword))
608 return false; // headersize - dont care
610 if(!read_uint(&map[0], fsize, &position, &dword))
612 return false; // width
622 if(!read_uint(&map[0], fsize, &position, &dword))
624 return false; // height
636 DALI_LOG_WARNING("Broken ICO file!\n");
639 // 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.
640 surface.Resize(w * h * 4);
641 memset(&surface[0], 0, w * h * 4);
643 if(!read_ushort(&map[0], fsize, &position, &word))
645 return false; // planes
648 if(!read_ushort(&map[0], fsize, &position, &word))
650 return false; // bitcount
652 unsigned int bitcount = word;
653 if(!read_uint(&map[0], fsize, &position, &dword))
655 return false; // compression
657 //compression = dword;
658 if(!read_uint(&map[0], fsize, &position, &dword))
660 return false; // imagesize
663 if(!read_uint(&map[0], fsize, &position, &dword))
665 return false; // z pixels per m
667 if(!read_uint(&map[0], fsize, &position, &dword))
669 return false; // y pizels per m
671 if(!read_uint(&map[0], fsize, &position, &dword))
673 return false; // colors used
675 //colorsused = dword;
676 if(!read_uint(&map[0], fsize, &position, &dword))
678 return false; // colors important
681 for(unsigned int i = 0; i < cols; i++)
683 unsigned char a, r, g, b;
685 if(!read_uchar(&map[0], fsize, &position, &b))
689 if(!read_uchar(&map[0], fsize, &position, &g))
693 if(!read_uchar(&map[0], fsize, &position, &r))
697 if(!read_uchar(&map[0], fsize, &position, &a))
701 pal[i] = ARGB_JOIN(0xff, b, g, r);
704 // This is the reference way of calculating the total number of bytes necessary to store one row of pixels.
705 unsigned int stride = (((bitcount * w) + 31) / 32) * 4;
706 unsigned int bitStride = ((w + 31) / 32) * 4;
708 // Pixbuf only ever contains one scanline worth of data.
709 pixbuf.Resize(stride);
710 maskbuf.Resize(bitStride * h);
712 // Handle different bits-per-pixel.
713 if(!HandleBitsPerPixel(bitcount, map, pix, surface, w, h, fsize, position, pixbuf, stride, pal))
718 // From the spec: If bpp is less than 32, there will be a 1bpp mask bitmap also.
719 if((bitcount < 32) && !ApplyMask(map, fsize, position, maskbuf, bitStride, w, h, pix, surface))
721 // Return false if not able to apply mask when the bpp is less than 32
725 bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
726 auto pixels = bitmap.GetBuffer();
727 memcpy(pixels, &surface[0], w * h * 4);
732 } // namespace TizenPlatform