2 * Copyright (c) 2022 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(const unsigned char* const& map, size_t length, size_t* position, unsigned short* ret)
87 if(DALI_UNLIKELY(*position + 2 > length))
91 b[0] = map[(*position)++];
92 b[1] = map[(*position)++];
93 *ret = (b[1] << 8) | b[0];
97 bool read_uint(const unsigned char* const& map, size_t length, size_t* position, unsigned int* ret)
102 if(DALI_UNLIKELY(*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(const unsigned char* const& map, size_t length, size_t* position, unsigned char* ret)
116 if(DALI_UNLIKELY(*position + 1 > length))
120 *ret = map[(*position)++];
124 bool read_mem(const unsigned char* const& map, size_t length, size_t* position, void* buffer, int size)
126 if(DALI_UNLIKELY(*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));
166 if(DALI_UNLIKELY(fp == NULL))
168 DALI_LOG_ERROR("Error loading bitmap\n");
175 if(DALI_UNLIKELY(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);
189 if(DALI_UNLIKELY(0u == fsize))
194 if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
196 DALI_LOG_ERROR("Error seeking ICO data\n");
200 if(DALI_UNLIKELY(fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER))) //6 + 16 + 40
204 map.ResizeUninitialized(fsize);
205 if(DALI_UNLIKELY(fread(&map[0], 1, fsize, fp) != fsize))
207 DALI_LOG_WARNING("image file read opeation error!\n");
211 const std::uint8_t* const inputBufferPtr = &map[0];
213 int search = BIGGEST;
214 unsigned short reserved, type, count;
215 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &reserved)))
219 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &type)))
223 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &count)))
227 if(DALI_UNLIKELY(!((reserved == 0) &&
228 ((type == ICON) || (type == CURSOR)) && (count != 0))))
234 bool have_choice = false;
236 for(unsigned short i = 0; i < count; i++)
238 unsigned char tw = 0, th = 0, tcols = 0;
239 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &tw)))
248 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &th)))
257 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &tcols)))
262 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &byte)))
266 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
276 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
286 // 0 colors means 256 for paletized modes.
287 // Note: We must not do this conversion for bpp greater than 8, as there is no palette.
288 if(bpp <= 8 && cols == 0)
294 unsigned int bmoffset, bmsize;
295 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &bmsize)))
299 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &bmoffset)))
303 if(DALI_UNLIKELY((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize)))
307 if(search == BIGGEST)
311 ((pdelta >= chosen.pdelta) &&
312 (((bpp >= 3) && (bpp >= chosen.bpp)) ||
313 ((bpp < 3) && (cols >= chosen.cols)))))
316 chosen.pdelta = pdelta;
321 chosen.planes = planes;
322 chosen.bmsize = bmsize;
323 chosen.bmoffset = bmoffset;
328 if(DALI_UNLIKELY(chosen.bmoffset == 0))
337 * @brief Handle the different bits per pixel
338 * @param[in] bitcount The bit count
339 * @param[in] inputBufferPtr The map to use
340 * @param[in/out] pix A reference to the pointer to the pix buffer
341 * @param[in/out] outputBufferPtr A reference to the surface buffer
342 * @param[in] width The width
343 * @param[in] height The height
344 * @param[in] fsize The file size
345 * @param[in/out] position The position in the file
346 * @param[/outin] pixbuf A reference to the pixbuf
347 * @param[in] stride The stride to use
348 * @param[in] palette The palette
350 bool HandleBitsPerPixel(
351 const unsigned int bitcount,
352 const std::uint8_t* const& inputBufferPtr,
354 std::uint32_t* const& outputBufferPtr,
355 const unsigned int width,
356 const unsigned int height,
357 const unsigned int fsize,
359 const unsigned int stride,
360 const Dali::Vector<unsigned int>& palette)
362 // Pixbuf only ever contains one scanline worth of data.
363 Dali::Vector<std::uint8_t> pixbuf;
364 pixbuf.ResizeUninitialized(stride);
365 std::uint8_t* lineBufferPtr = &pixbuf[0];
367 // Note: Switch is in order of most common format first.
372 const std::uint8_t* p = inputBufferPtr + position;
373 pix = outputBufferPtr + ((height - 1) * width);
375 for(unsigned int i = 0; i < height; i++)
377 for(unsigned int j = 0; j < width; j++)
379 *pix++ = ARGB_JOIN(p[3], p[0], p[1], p[2]);
382 // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
390 for(unsigned int i = 0; i < height; i++)
392 pix = outputBufferPtr + ((height - 1 - i) * width);
393 if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
397 const std::uint8_t* p = lineBufferPtr;
398 for(unsigned int j = 0; j < width; j++)
400 *pix++ = ARGB_JOIN(0xff, p[0], p[1], p[2]);
409 for(unsigned int i = 0; i < height; i++)
411 pix = outputBufferPtr + ((height - 1 - i) * width);
412 if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
416 const std::uint8_t* p = lineBufferPtr;
417 for(unsigned int j = 0; j < width; j++)
419 *pix++ = palette[*p++];
427 for(unsigned int i = 0; i < height; i++)
429 pix = outputBufferPtr + ((height - 1 - i) * width);
430 if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
434 const std::uint8_t* p = lineBufferPtr;
435 for(unsigned int j = 0; j < width; j++)
439 *pix = palette[*p & 0x0f];
444 *pix = palette[*p >> 4];
454 const std::uint32_t bytesPerWidth = width / 8;
455 const std::uint32_t bytesRemainingPerWidth = width & 7;
456 for(unsigned int i = 0; i < height; i++)
458 pix = outputBufferPtr + ((height - 1 - i) * width);
459 if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, stride)))
464 const std::uint8_t* p = lineBufferPtr;
465 for(unsigned int j = 0; j < bytesPerWidth; ++j)
467 *pix++ = palette[*p >> 7];
468 *pix++ = palette[*p >> 6 & 0x01];
469 *pix++ = palette[*p >> 5 & 0x01];
470 *pix++ = palette[*p >> 4 & 0x01];
471 *pix++ = palette[*p >> 3 & 0x01];
472 *pix++ = palette[*p >> 2 & 0x01];
473 *pix++ = palette[*p >> 1 & 0x01];
474 *pix++ = palette[*p >> 0 & 0x01];
478 if(bytesRemainingPerWidth > 0)
480 for(std::uint32_t j = 0; j < bytesRemainingPerWidth; ++j)
482 *pix++ = palette[(*p >> (7 - j)) & 0x01];
492 DALI_LOG_WARNING("Image file contains unsupported bits-per-pixel %d\n", bitcount);
501 * @brief Apply the mask if required
502 * @param[in] inputBufferPtr The map to use
503 * @param[in] fsize The file size
504 * @param[in/out] position The position in the file
505 * @param[in] bitStride The stride
506 * @param[in] width The width
507 * @param[in] height The height
508 * @param[in/out] pix A reference to the pointer to the pix buffer
509 * @param[in/out] outputBufferPtr A reference to the surface buffer
512 const std::uint8_t* const& inputBufferPtr,
513 const unsigned int fsize,
515 const unsigned int bitStride,
516 const unsigned int width,
517 const unsigned int height,
519 std::uint32_t* const& outputBufferPtr)
521 Dali::Vector<std::uint8_t> maskbuf;
522 maskbuf.ResizeUninitialized(bitStride);
523 std::uint8_t* lineBufferPtr = &maskbuf[0];
526 // Precalc to save time in the loops.
527 unsigned int bytesPerWidth = width / 8;
528 unsigned int bytesRemainingPerWidth = width - (bytesPerWidth << 3);
530 // Loop for each line of the image.
531 for(unsigned int i = 0; i < height; ++i)
533 pix = outputBufferPtr + ((height - 1 - i) * width);
534 if(DALI_UNLIKELY(!read_mem(inputBufferPtr, fsize, &position, lineBufferPtr, bitStride)))
538 const std::uint8_t* m = lineBufferPtr;
540 // Do chunks of 8 pixels first so mask operations can be unrolled.
541 for(unsigned int j = 0; j < bytesPerWidth; ++j)
543 // Unrolled 8 bits of the mask to avoid many conditions and branches.
544 A_VAL(pix++) = (*m & (1 << 7)) ? 0x00 : 0xff;
545 A_VAL(pix++) = (*m & (1 << 6)) ? 0x00 : 0xff;
546 A_VAL(pix++) = (*m & (1 << 5)) ? 0x00 : 0xff;
547 A_VAL(pix++) = (*m & (1 << 4)) ? 0x00 : 0xff;
548 A_VAL(pix++) = (*m & (1 << 3)) ? 0x00 : 0xff;
549 A_VAL(pix++) = (*m & (1 << 2)) ? 0x00 : 0xff;
550 A_VAL(pix++) = (*m & (1 << 1)) ? 0x00 : 0xff;
551 A_VAL(pix++) = (*m & (1 << 0)) ? 0x00 : 0xff;
555 // Handle any remaining width ( < 8 ) or images that are < 8 wide.
556 if(bytesRemainingPerWidth > 0)
558 for(unsigned int j = 0; j < bytesRemainingPerWidth; ++j)
560 // Note: Although we are doing less that a bytes worth of mask, we still always start on the first bit.
561 // If the image is smaller than 8 pixels wide, each mask will still start on a new byte.
562 A_VAL(pix++) = (*m & (1 << (7 - j))) ? 0x00 : 0xff;
571 } //unnamed namespace
573 bool LoadIcoHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
576 Dali::Vector<unsigned char> map;
578 FILE* const fp = input.file;
580 if(DALI_UNLIKELY(false == LoadIcoHeaderHelper(fp, chosen, map, fsize)))
591 bool LoadBitmapFromIco(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
594 Dali::Vector<unsigned char> map;
596 FILE* const fp = input.file;
598 if(DALI_UNLIKELY(false == LoadIcoHeaderHelper(fp, chosen, map, fsize)))
609 size_t position = chosen.bmoffset; //22 == position
611 unsigned int w = chosen.w;
612 unsigned int h = chosen.h;
613 unsigned int cols = chosen.cols;
615 const std::uint8_t* const inputBufferPtr = &map[0];
617 // read bmp header time... let's do some checking
618 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
620 return false; // headersize - dont care
622 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
624 return false; // width
634 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
636 return false; // height
648 DALI_LOG_WARNING("Broken ICO file!\n");
651 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
653 return false; // planes
656 if(DALI_UNLIKELY(!read_ushort(inputBufferPtr, fsize, &position, &word)))
658 return false; // bitcount
660 unsigned int bitcount = word;
661 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
663 return false; // compression
665 //compression = dword;
666 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
668 return false; // imagesize
671 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
673 return false; // z pixels per m
675 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
677 return false; // y pizels per m
679 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
681 return false; // colors used
683 //colorsused = dword;
684 if(DALI_UNLIKELY(!read_uint(inputBufferPtr, fsize, &position, &dword)))
686 return false; // colors important
689 Dali::Vector<unsigned int> pal;
691 for(unsigned int i = 0; i < cols; i++)
693 unsigned char a, r, g, b;
695 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &b)))
699 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &g)))
703 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &r)))
707 if(DALI_UNLIKELY(!read_uchar(inputBufferPtr, fsize, &position, &a)))
711 pal[i] = ARGB_JOIN(0xff, b, g, r);
714 Dali::Vector<std::uint32_t> surface;
716 // This is the reference way of calculating the total number of bytes necessary to store one row of pixels.
717 unsigned int stride = (((bitcount * w) + 31) / 32) * 4;
718 unsigned int bitStride = ((w + 31) / 32) * 4;
719 // Set up the surface as soon as we have the width and height.
720 surface.ResizeUninitialized(w * h);
722 std::uint32_t* const outputBufferPtr = &surface[0];
724 // Handle different bits-per-pixel.
725 if(DALI_UNLIKELY(!HandleBitsPerPixel(bitcount, inputBufferPtr, pix, outputBufferPtr, w, h, fsize, position, stride, pal)))
730 // From the spec: If bpp is less than 32, there will be a 1bpp mask bitmap also.
733 if(DALI_UNLIKELY(!ApplyMask(inputBufferPtr, fsize, position, bitStride, w, h, pix, outputBufferPtr)))
735 // Return false if not able to apply mask when the bpp is less than 32
740 bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
741 auto pixels = bitmap.GetBuffer();
742 memcpy(pixels, outputBufferPtr, w * h * 4);
747 } // namespace TizenPlatform