Wbmp load faster
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-wbmp.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 // HEADER
19 #include <dali/internal/imaging/common/loader-wbmp.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstdio>
23 #include <cstdlib>
24 #include <cstring>
25
26 // INTERNAL INCLUDES
27 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
28 #include <dali/integration-api/debug.h>
29
30 namespace Dali
31 {
32 namespace TizenPlatform
33 {
34 namespace
35 {
36 #if defined(DEBUG_ENABLED)
37 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_LOADER_WBMP");
38 #endif
39
40 // TODO : We need to determine it in dali-common.h or something else. Currently, we set this value in code level.
41 #define DALI_BYTE_ORDER_BIG_ENDIAN 0
42
43 #define IMG_MAX_SIZE 65536
44
45 #define IMG_TOO_BIG(w, h)                                 \
46   ((((unsigned long long)w) * ((unsigned long long)h)) >= \
47    ((1ULL << (29)) - 2048))
48
49 //extract multiple bytes integer , and saved in *data
50 int extractMultiByteInteger(unsigned int* data, void* map, size_t length, size_t* position)
51 {
52   // the header field contains an image type indentifier of multi-byte length(TypeField), an octet of general header info(FixHeaderField)
53   //,  a multi-byte width field(Width) and a multi-byte height field(Height) and so on.
54   // The actual organisation of the image data depends on the image type
55   // for Ext Headers flag (7th bit), 1 = More will follow, 0 = Last octet
56   // so in the for loop, if(buf & 0x80 == 0), loop will be exited
57   int           targetMultiByteInteger = 0, readBufCount;
58   unsigned char buf;
59
60   for(readBufCount = 0;;)
61   {
62     // readBufCount means the count that fetched data from map
63     // extractMultiByteInteger() is to fetch wbmp type , width, and height
64     // for wbmp type, when readBufCount == 1, buf = 0x00, it will exit the loop
65     // for width, it have 4 bytes, so when readBufCount == 4, it must exit the loop
66     // for general width and height, if(buf & 0x80) == 0, then the next byte does not need to fetch again
67     // first step, readBufCount = 1 , read int(4 bytes) to buf, if buf & 0x80 !=0, the buf need to continue to fetch
68     // second step, readBufCount = 2, read next( 4 bytes) to buf, if buf & 0x80 == 0, then assigned the buf to target
69     if((readBufCount++) == 4)
70     {
71       return -1;
72     }
73     if(*position > length)
74     {
75       return -1;
76     }
77     buf                    = reinterpret_cast<unsigned char*>(map)[(*position)++];
78     targetMultiByteInteger = (targetMultiByteInteger << 7) | (buf & 0x7f);
79
80     if((buf & 0x80) == 0)
81     {
82       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "position: %d, readBufCount: %d\n", *position, readBufCount);
83       break;
84     }
85   }
86   *data = targetMultiByteInteger;
87   return 0;
88 }
89
90 // Calculate 4bit integer into 4byte integer
91 constexpr std::uint32_t Calculate4BitTo4Byte(const std::uint8_t& input)
92 {
93   std::uint32_t output = 0;
94 #if DALI_BYTE_ORDER_BIG_ENDIAN
95   output |= static_cast<std::uint32_t>(input & 0x08) << 21;
96   output |= static_cast<std::uint32_t>(input & 0x04) << 14;
97   output |= static_cast<std::uint32_t>(input & 0x02) << 7;
98   output |= static_cast<std::uint32_t>(input & 0x01);
99 #else
100   output |= static_cast<std::uint32_t>(input & 0x08) >> 3;
101   output |= static_cast<std::uint32_t>(input & 0x04) << 6;
102   output |= static_cast<std::uint32_t>(input & 0x02) << 15;
103   output |= static_cast<std::uint32_t>(input & 0x01) << 24;
104 #endif
105   return output * 0xff;
106 }
107
108 /**
109  * @brief Calculation result bit-->byte table in compile.
110  * Required memory = 16 * 4byte = 64byte
111  */
112 // clang-format off
113 constexpr std::uint32_t cachedCalculation4BitTo4ByteTable[16] = {
114   Calculate4BitTo4Byte(0x00), Calculate4BitTo4Byte(0x01), Calculate4BitTo4Byte(0x02), Calculate4BitTo4Byte(0x03),
115   Calculate4BitTo4Byte(0x04), Calculate4BitTo4Byte(0x05), Calculate4BitTo4Byte(0x06), Calculate4BitTo4Byte(0x07),
116   Calculate4BitTo4Byte(0x08), Calculate4BitTo4Byte(0x09), Calculate4BitTo4Byte(0x0a), Calculate4BitTo4Byte(0x0b),
117   Calculate4BitTo4Byte(0x0c), Calculate4BitTo4Byte(0x0d), Calculate4BitTo4Byte(0x0e), Calculate4BitTo4Byte(0x0f)};
118 // clang-format on
119 } // end unnamed namespace
120
121 bool LoadBitmapFromWbmp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
122 {
123   FILE* const fp = input.file;
124   if(fp == NULL)
125   {
126     DALI_LOG_ERROR("Error loading bitmap\n");
127     return false;
128   }
129   Dali::Vector<unsigned char> map;
130   size_t                      position = 0;
131
132   std::uint32_t w, h;
133   std::uint32_t type;
134   std::uint32_t lineByteLength;
135
136   if(fseek(fp, 0, SEEK_END))
137   {
138     DALI_LOG_ERROR("Error seeking WBMP data\n");
139     return false;
140   }
141   long positionIndicator = ftell(fp);
142
143   unsigned int fsize(0u);
144   if(positionIndicator > -1L)
145   {
146     fsize = static_cast<unsigned int>(positionIndicator);
147   }
148
149   if(0u == fsize)
150   {
151     DALI_LOG_ERROR("Error: filesize is 0!\n");
152     return false;
153   }
154
155   if(fseek(fp, 0, SEEK_SET))
156   {
157     DALI_LOG_ERROR("Error seeking WBMP data\n");
158     return false;
159   }
160   if(fsize <= 4)
161   {
162     DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
163     return false;
164   }
165   if(fsize > 4096 * 4096 * 4)
166   {
167     DALI_LOG_ERROR("Error: WBMP size is too large!\n");
168     return false;
169   }
170   map.ResizeUninitialized(fsize);
171
172   if(fread(&map[0], 1, fsize, fp) != fsize)
173   {
174     DALI_LOG_WARNING("image file read opeation error!\n");
175     return false;
176   }
177
178   if(extractMultiByteInteger(&type, &map[0], fsize, &position) < 0)
179   {
180     return false;
181   }
182
183   position++; /* skipping one byte */
184
185   if(extractMultiByteInteger(&w, &map[0], fsize, &position) < 0)
186   {
187     return false;
188   }
189   if(extractMultiByteInteger(&h, &map[0], fsize, &position) < 0)
190   {
191     return false;
192   }
193   if(type != 0)
194   {
195     DALI_LOG_ERROR("Unknown Format!\n");
196     return false;
197   }
198
199   if((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
200   {
201     return false;
202   }
203
204   lineByteLength = (w + 7) >> 3;
205   // fsize was wrong! Load failed.
206   if(DALI_UNLIKELY(position + h * lineByteLength > fsize))
207   {
208     DALI_LOG_ERROR("Pixel infomation is bigger than file size! (%u + %u * %u > %u)\n", static_cast<std::uint32_t>(position), h, lineByteLength, fsize);
209     return false;
210   }
211
212   // w >= 1 and h >= 1. So we can assume that outputPixels is not null.
213   auto outputPixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
214   /**
215    * @code
216    * std::uint8_t* line = NULL;
217    * std::uint32_t cur  = 0, x, y;
218    * for(y = 0; y < h; y++)
219    * {
220    *   line = &map[0] + position;
221    *   position += lineByteLength;
222    *   for(x = 0; x < w; x++)
223    *   {
224    *     int idx    = x >> 3;
225    *     int offset = 1 << (0x07 - (x & 0x07));
226    *     if(line[idx] & offset)
227    *     {
228    *       outputPixels[cur] = 0xff; //0xffffffff;
229    *     }
230    *     else
231    *     {
232    *       outputPixels[cur] = 0x00; //0xff000000;
233    *     }
234    *     cur++;
235    *   }
236    * }
237    * @endcode
238    */
239
240   const std::uint8_t* inputPixels                  = &map[0] + position;
241   const std::uint32_t lineByteLengthWithoutPadding = w >> 3;
242   const std::uint8_t  linePadding                  = w & 0x07;
243
244   for(std::uint32_t y = 0; y < h; y++)
245   {
246     for(std::uint32_t x = 0; x < lineByteLengthWithoutPadding; x++)
247     {
248       // memset whole 8 bits
249       // outputPixels filled 4 bytes in one operation.
250       // cachedCalculation4BitTo4ByteTable calculated in compile-time.
251       *(reinterpret_cast<std::uint32_t*>(outputPixels + 0)) = cachedCalculation4BitTo4ByteTable[((*inputPixels) >> 4) & 0x0f];
252       *(reinterpret_cast<std::uint32_t*>(outputPixels + 4)) = cachedCalculation4BitTo4ByteTable[(*inputPixels) & 0x0f];
253       outputPixels += 8;
254       ++inputPixels;
255     }
256     if(linePadding > 0)
257     {
258       // memset linePadding bits naive.
259       for(std::uint8_t x = 0; x < linePadding; ++x)
260       {
261         const std::uint8_t offset = (0x07 - (x & 0x07));
262         *outputPixels             = ((*inputPixels) >> offset) & 1 ? 0xff : 0x00;
263         ++outputPixels;
264       }
265       ++inputPixels;
266     }
267   }
268
269   return true;
270 }
271
272 bool LoadWbmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
273 {
274   FILE* const fp = input.file;
275   if(fp == NULL)
276   {
277     DALI_LOG_ERROR("Error loading bitmap\n");
278     return false;
279   }
280   Dali::Vector<unsigned char> map;
281   size_t                      position = 0;
282
283   unsigned int w, h;
284   unsigned int type;
285   if(fseek(fp, 0, SEEK_END))
286   {
287     DALI_LOG_ERROR("Error seeking WBMP data\n");
288     return false;
289   }
290   long positionIndicator = ftell(fp);
291
292   unsigned int fsize(0u);
293   if(positionIndicator > -1L)
294   {
295     fsize = static_cast<unsigned int>(positionIndicator);
296   }
297
298   if(0u == fsize)
299   {
300     return false;
301   }
302
303   if(fseek(fp, 0, SEEK_SET))
304   {
305     DALI_LOG_ERROR("Error seeking WBMP data\n");
306     return false;
307   }
308   if(fsize <= 4)
309   {
310     DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
311     return false;
312   }
313
314   // type(1 byte) + fixedheader(1 byte) + width(uint) + height(uint)
315   unsigned int headerSize = 1 + 1 + 4 + 4; // 8 + 8 + 32 + 32;
316   headerSize              = std::min(headerSize, fsize);
317
318   map.ResizeUninitialized(headerSize);
319   if(fread(&map[0], 1, headerSize, fp) != headerSize)
320   {
321     DALI_LOG_WARNING("image file read opeation error!\n");
322     return false;
323   }
324
325   if(extractMultiByteInteger(&type, &map[0], headerSize, &position) < 0)
326   {
327     DALI_LOG_ERROR("Error: unable to read type!\n");
328     return false;
329   }
330   position++; /* skipping one byte */
331   if(type != 0)
332   {
333     DALI_LOG_ERROR("Error: unknown format!\n");
334     return false;
335   }
336   if(extractMultiByteInteger(&w, &map[0], headerSize, &position) < 0)
337   {
338     DALI_LOG_ERROR("Error: can not read width!\n");
339     return false;
340   }
341   if(extractMultiByteInteger(&h, &map[0], headerSize, &position) < 0)
342   {
343     DALI_LOG_ERROR("Error: can not read height!\n");
344     return false;
345   }
346
347   if((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
348   {
349     DALI_LOG_ERROR("Error: file size is not supported!\n");
350     return false;
351   }
352
353   width  = w;
354   height = h;
355   return true;
356 }
357
358 } // namespace TizenPlatform
359 } // namespace Dali