Merge "Cache default fontDescription + use default when fontDescription empty" into...
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-ico.cpp
1 /*
2  * Copyright (c) 2022 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 <dali/internal/imaging/common/loader-ico.h>
52
53 // EXTERNAL INCLUDES
54 #include <dali/public-api/common/dali-vector.h>
55 #include <cstring>
56
57 // INTERNAL INCLUDES
58 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
59 #include <dali/integration-api/debug.h>
60
61 namespace Dali
62 {
63 namespace TizenPlatform
64 {
65 namespace
66 {
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;
73
74 typedef unsigned char DATA8;
75 #define A_VAL(p) (reinterpret_cast<DATA8*>(p)[3])
76
77 #define RGB_JOIN(r, g, b) \
78   (((r) << 16) + ((g) << 8) + (b))
79
80 #define ARGB_JOIN(a, r, g, b) \
81   (((a) << 24) + ((r) << 16) + ((g) << 8) + (b))
82
83 bool read_ushort(unsigned char* map, size_t length, size_t* position, unsigned short* ret)
84 {
85   unsigned char b[2];
86
87   if(*position + 2 > length)
88   {
89     return false;
90   }
91   b[0] = map[(*position)++];
92   b[1] = map[(*position)++];
93   *ret = (b[1] << 8) | b[0];
94   return true;
95 }
96
97 bool read_uint(unsigned char* map, size_t length, size_t* position, unsigned int* ret)
98 {
99   unsigned char b[4];
100   unsigned int  i;
101
102   if(*position + 4 > length)
103   {
104     return false;
105   }
106   for(i = 0; i < 4; i++)
107   {
108     b[i] = map[(*position)++];
109   }
110   *ret = ARGB_JOIN(b[3], b[2], b[1], b[0]);
111   return true;
112 }
113
114 bool read_uchar(unsigned char* map, size_t length, size_t* position, unsigned char* ret)
115 {
116   if(*position + 1 > length)
117   {
118     return false;
119   }
120   *ret = map[(*position)++];
121   return true;
122 }
123
124 bool read_mem(unsigned char* map, size_t length, size_t* position, void* buffer, int size)
125 {
126   if(*position + size > length)
127   {
128     return false;
129   }
130   memcpy(buffer, map + *position, size);
131   *position += size;
132   return true;
133 }
134
135 enum
136 {
137   SMALLEST,
138   BIGGEST,
139   SMALLER,
140   BIGGER
141 };
142
143 enum
144 {
145   ICON   = 1,
146   CURSOR = 2
147 };
148
149 struct IcoData
150 {
151   int          pdelta;
152   int          w, h;
153   int          cols;
154   int          bpp, planes;
155   int          hot_x, hot_y;
156   unsigned int bmoffset, bmsize;
157 };
158
159 bool LoadIcoHeaderHelper(FILE*                        fp,
160                          IcoData&                     chosen,
161                          Dali::Vector<unsigned char>& map,
162                          unsigned int&                fsize)
163 {
164   memset(&chosen, 0, sizeof(chosen));
165
166   if(fp == NULL)
167   {
168     DALI_LOG_ERROR("Error loading bitmap\n");
169     return false;
170   }
171   size_t         position = 0;
172   unsigned short word;
173   unsigned char  byte;
174
175   if(fseek(fp, 0, SEEK_END))
176   {
177     DALI_LOG_ERROR("Error seeking ICO data\n");
178     return false;
179   }
180
181   long positionIndicator = ftell(fp);
182   fsize                  = 0u;
183
184   if(positionIndicator > -1L)
185   {
186     fsize = static_cast<unsigned int>(positionIndicator);
187   }
188
189   if(0u == fsize)
190   {
191     return false;
192   }
193
194   if(fseek(fp, 0, SEEK_SET))
195   {
196     DALI_LOG_ERROR("Error seeking ICO data\n");
197     return false;
198   }
199
200   if(fsize < (ICO_FILE_HEADER + ICO_IMAGE_INFO_HEADER)) //6 + 16 + 40
201   {
202     return false;
203   }
204   map.ResizeUninitialized(fsize);
205
206   if(fread(&map[0], 1, fsize, fp) != fsize)
207   {
208     DALI_LOG_WARNING("image file read opeation error!\n");
209     return false;
210   }
211
212   int            search = BIGGEST;
213   unsigned short reserved, type, count;
214   if(!read_ushort(&map[0], fsize, &position, &reserved))
215   {
216     return false;
217   }
218   if(!read_ushort(&map[0], fsize, &position, &type))
219   {
220     return false;
221   }
222   if(!read_ushort(&map[0], fsize, &position, &count))
223   {
224     return false;
225   }
226   if(!((reserved == 0) &&
227        ((type == ICON) || (type == CURSOR)) && (count != 0)))
228   {
229     return false;
230   }
231   search           = BIGGEST;
232   chosen.pdelta    = 0;
233   bool have_choice = false;
234
235   for(unsigned short i = 0; i < count; i++)
236   {
237     unsigned char tw = 0, th = 0, tcols = 0;
238     if(!read_uchar(&map[0], fsize, &position, &tw))
239     {
240       return false;
241     }
242     int w = tw;
243     if(w <= 0)
244     {
245       w = 256;
246     }
247     if(!read_uchar(&map[0], fsize, &position, &th))
248     {
249       return false;
250     }
251     int h = th;
252     if(h <= 0)
253     {
254       h = 256;
255     }
256     if(!read_uchar(&map[0], fsize, &position, &tcols))
257     {
258       return false;
259     }
260     int cols = tcols;
261     if(!read_uchar(&map[0], fsize, &position, &byte))
262     {
263       return false;
264     }
265     if(!read_ushort(&map[0], fsize, &position, &word))
266     {
267       return false;
268     }
269     int planes = 0;
270     if(type == 1)
271     {
272       planes = word;
273     }
274     //else hot_x = word;
275     if(!read_ushort(&map[0], fsize, &position, &word))
276     {
277       return false;
278     }
279     int bpp = 0;
280     if(type == 1)
281     {
282       bpp = word;
283     }
284
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)
288     {
289       cols = 256;
290     }
291
292     //else hot_y = word;
293     unsigned int bmoffset, bmsize;
294     if(!read_uint(&map[0], fsize, &position, &bmsize))
295     {
296       return false;
297     }
298     if(!read_uint(&map[0], fsize, &position, &bmoffset))
299     {
300       return false;
301     }
302     if((bmsize <= 0) || (bmoffset <= 0) || (bmoffset >= fsize))
303     {
304       return false;
305     }
306     if(search == BIGGEST)
307     {
308       int pdelta = w * h;
309       if((!have_choice) ||
310          ((pdelta >= chosen.pdelta) &&
311           (((bpp >= 3) && (bpp >= chosen.bpp)) ||
312            ((bpp < 3) && (cols >= chosen.cols)))))
313       {
314         have_choice     = true;
315         chosen.pdelta   = pdelta;
316         chosen.w        = w;
317         chosen.h        = h;
318         chosen.cols     = cols;
319         chosen.bpp      = bpp;
320         chosen.planes   = planes;
321         chosen.bmsize   = bmsize;
322         chosen.bmoffset = bmoffset;
323       }
324     }
325   }
326
327   if(chosen.bmoffset == 0)
328   {
329     return false;
330   }
331
332   return true;
333 }
334
335 /**
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
348  */
349 bool HandleBitsPerPixel(
350   const unsigned int                bitcount,
351   Dali::Vector<unsigned char>&      map,
352   unsigned int*&                    pix,
353   Dali::Vector<unsigned int>&       surface,
354   const unsigned int                width,
355   const unsigned int                height,
356   const unsigned int                fsize,
357   size_t&                           position,
358   Dali::Vector<unsigned char>&      pixbuf,
359   const unsigned int                stride,
360   const Dali::Vector<unsigned int>& palette)
361 {
362   // Note: Switch is in order of most common format first.
363   switch(bitcount)
364   {
365     case 32:
366     {
367       unsigned char* p = &map[position];
368       pix              = &surface[0] + ((height - 1) * width);
369
370       for(unsigned int i = 0; i < height; i++)
371       {
372         for(unsigned int j = 0; j < width; j++)
373         {
374           *pix++ = ARGB_JOIN(p[3], p[0], p[1], p[2]);
375           p += 4;
376         }
377         // Move the output up 1 line (we subtract 2 lines because we moved forward one line while copying).
378         pix -= (width * 2);
379       }
380       break;
381     }
382
383     case 24:
384     {
385       for(unsigned int i = 0; i < height; i++)
386       {
387         pix = &surface[0] + ((height - 1 - i) * width);
388         if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
389         {
390           return false;
391         }
392         unsigned char* p = &pixbuf[0];
393         for(unsigned int j = 0; j < width; j++)
394         {
395           *pix++ = ARGB_JOIN(0xff, p[0], p[1], p[2]);
396           p += 3;
397         }
398       }
399       break;
400     }
401
402     case 8:
403     {
404       for(unsigned int i = 0; i < height; i++)
405       {
406         pix = &surface[0] + ((height - 1 - i) * width);
407         if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
408         {
409           return false;
410         }
411         unsigned char* p = &pixbuf[0];
412         for(unsigned int j = 0; j < width; j++)
413         {
414           *pix++ = palette[*p++];
415         }
416       }
417       break;
418     }
419
420     case 4:
421     {
422       for(unsigned int i = 0; i < height; i++)
423       {
424         pix = &surface[0] + ((height - 1 - i) * width);
425         if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
426         {
427           return false;
428         }
429         unsigned char* p = &pixbuf[0];
430         for(unsigned int j = 0; j < width; j++)
431         {
432           if(j & 0x1)
433           {
434             *pix = palette[*p & 0x0f];
435             p++;
436           }
437           else
438           {
439             *pix = palette[*p >> 4];
440           }
441           pix++;
442         }
443       }
444       break;
445     }
446
447     case 1:
448     {
449       for(unsigned int i = 0; i < height; i++)
450       {
451         pix = &surface[0] + ((height - 1 - i) * width);
452         if(!read_mem(&map[0], fsize, &position, &pixbuf[0], stride))
453         {
454           return false;
455         }
456         unsigned char* p = &pixbuf[0];
457
458         for(unsigned int j = 0; j < width; j += 8)
459         {
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];
468
469           p++;
470         }
471       }
472       break;
473     }
474
475     default:
476     {
477       DALI_LOG_WARNING("Image file contains unsupported bits-per-pixel %d\n", bitcount);
478       return false;
479     }
480   }
481
482   return true;
483 }
484
485 /**
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
496  */
497 bool ApplyMask(
498   Dali::Vector<unsigned char>& map,
499   const unsigned int           fsize,
500   size_t&                      position,
501   Dali::Vector<unsigned char>& maskbuf,
502   const unsigned int           bitStride,
503   const unsigned int           width,
504   const unsigned int           height,
505   unsigned int*&               pix,
506   Dali::Vector<unsigned int>&  surface)
507 {
508   if(!read_mem(&map[0], fsize, &position, &maskbuf[0], bitStride * height))
509   {
510     return false;
511   }
512
513   // Apply mask.
514   // Precalc to save time in the loops.
515   unsigned int bytesPerWidth          = width / 8;
516   unsigned int bytesRemainingPerWidth = width - (bytesPerWidth << 3);
517
518   // Loop for each line of the image.
519   for(unsigned int i = 0; i < height; ++i)
520   {
521     unsigned char* m = &maskbuf[0] + (bitStride * i);
522     pix              = &surface[0] + ((height - 1 - i) * width);
523
524     // Do chunks of 8 pixels first so mask operations can be unrolled.
525     for(unsigned int j = 0; j < bytesPerWidth; ++j)
526     {
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;
536       m++;
537     }
538
539     // Handle any remaining width ( < 8 ) or images that are < 8 wide.
540     if(bytesRemainingPerWidth > 0)
541     {
542       for(unsigned int j = 0; j < bytesRemainingPerWidth; ++j)
543       {
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;
547       }
548       m++;
549     }
550   }
551
552   return true;
553 }
554
555 } //unnamed namespace
556
557 bool LoadIcoHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
558 {
559   IcoData                     chosen;
560   Dali::Vector<unsigned char> map;
561   unsigned int                fsize;
562   FILE* const                 fp = input.file;
563
564   if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
565   {
566     return false;
567   }
568
569   width  = chosen.w;
570   height = chosen.h;
571
572   return true;
573 }
574
575 bool LoadBitmapFromIco(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
576 {
577   IcoData                     chosen;
578   Dali::Vector<unsigned char> map;
579   unsigned int                fsize;
580   FILE* const                 fp = input.file;
581
582   if(false == LoadIcoHeaderHelper(fp, chosen, map, fsize))
583   {
584     return false;
585   }
586
587   Dali::Vector<unsigned int>  pal;
588   Dali::Vector<unsigned int>  surface;
589   Dali::Vector<unsigned char> maskbuf;
590   Dali::Vector<unsigned char> pixbuf;
591   pal.Resize(256 * 4);
592
593   unsigned int   dword;
594   unsigned short word;
595
596   int           diff_size = 0;
597   unsigned int* pix;
598
599   size_t position = chosen.bmoffset; //22 == position
600
601   unsigned int w    = chosen.w;
602   unsigned int h    = chosen.h;
603   unsigned int cols = chosen.cols;
604
605   // read bmp header time... let's do some checking
606   if(!read_uint(&map[0], fsize, &position, &dword))
607   {
608     return false; // headersize - dont care
609   }
610   if(!read_uint(&map[0], fsize, &position, &dword))
611   {
612     return false; // width
613   }
614   if(dword > 0)
615   {
616     if(dword != w)
617     {
618       w         = dword;
619       diff_size = 1;
620     }
621   }
622   if(!read_uint(&map[0], fsize, &position, &dword))
623   {
624     return false; // height
625   }
626   if(dword > 0)
627   {
628     if(dword != (h * 2))
629     {
630       h         = dword / 2;
631       diff_size = 1;
632     }
633   }
634   if(diff_size)
635   {
636     DALI_LOG_WARNING("Broken ICO file!\n");
637   }
638
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.ResizeUninitialized(w * h * 4);
641   memset(&surface[0], 0, w * h * 4);
642
643   if(!read_ushort(&map[0], fsize, &position, &word))
644   {
645     return false; // planes
646   }
647   //planes2 = word;
648   if(!read_ushort(&map[0], fsize, &position, &word))
649   {
650     return false; // bitcount
651   }
652   unsigned int bitcount = word;
653   if(!read_uint(&map[0], fsize, &position, &dword))
654   {
655     return false; // compression
656   }
657   //compression = dword;
658   if(!read_uint(&map[0], fsize, &position, &dword))
659   {
660     return false; // imagesize
661   }
662   //imagesize = dword;
663   if(!read_uint(&map[0], fsize, &position, &dword))
664   {
665     return false; // z pixels per m
666   }
667   if(!read_uint(&map[0], fsize, &position, &dword))
668   {
669     return false; // y pizels per m
670   }
671   if(!read_uint(&map[0], fsize, &position, &dword))
672   {
673     return false; // colors used
674   }
675   //colorsused = dword;
676   if(!read_uint(&map[0], fsize, &position, &dword))
677   {
678     return false; // colors important
679   }
680
681   for(unsigned int i = 0; i < cols; i++)
682   {
683     unsigned char a, r, g, b;
684
685     if(!read_uchar(&map[0], fsize, &position, &b))
686     {
687       return false;
688     }
689     if(!read_uchar(&map[0], fsize, &position, &g))
690     {
691       return false;
692     }
693     if(!read_uchar(&map[0], fsize, &position, &r))
694     {
695       return false;
696     }
697     if(!read_uchar(&map[0], fsize, &position, &a))
698     {
699       return false;
700     }
701     pal[i] = ARGB_JOIN(0xff, b, g, r);
702   }
703
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;
707
708   // Pixbuf only ever contains one scanline worth of data.
709   pixbuf.ResizeUninitialized(stride);
710   maskbuf.ResizeUninitialized(bitStride * h);
711
712   // Handle different bits-per-pixel.
713   if(!HandleBitsPerPixel(bitcount, map, pix, surface, w, h, fsize, position, pixbuf, stride, pal))
714   {
715     return false;
716   }
717
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))
720   {
721     // Return false if not able to apply mask when the bpp is less than 32
722     return false;
723   }
724
725   bitmap      = Dali::Devel::PixelBuffer::New(w, h, Pixel::Format::RGBA8888);
726   auto pixels = bitmap.GetBuffer();
727   memcpy(pixels, &surface[0], w * h * 4);
728
729   return true;
730 }
731
732 } // namespace TizenPlatform
733
734 } // namespace Dali