Minor optimization on image loading
[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, const std::uint8_t* const& 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(DALI_UNLIKELY((readBufCount++) == 4))
70     {
71       return -1;
72     }
73     if(DALI_UNLIKELY(*position > length))
74     {
75       return -1;
76     }
77     buf                    = 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(DALI_UNLIKELY(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(DALI_UNLIKELY(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(DALI_UNLIKELY(0u == fsize))
150   {
151     DALI_LOG_ERROR("Error: filesize is 0!\n");
152     return false;
153   }
154
155   if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
156   {
157     DALI_LOG_ERROR("Error seeking WBMP data\n");
158     return false;
159   }
160   if(DALI_UNLIKELY(fsize <= 4))
161   {
162     DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
163     return false;
164   }
165   if(DALI_UNLIKELY(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(DALI_UNLIKELY(fread(&map[0], 1, fsize, fp) != fsize))
173   {
174     DALI_LOG_WARNING("image file read opeation error!\n");
175     return false;
176   }
177
178   const std::uint8_t* const inputBufferPtr = &map[0];
179
180   if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, fsize, &position) < 0))
181   {
182     return false;
183   }
184
185   position++; /* skipping one byte */
186
187   if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, fsize, &position) < 0))
188   {
189     return false;
190   }
191   if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, fsize, &position) < 0))
192   {
193     return false;
194   }
195   if(DALI_UNLIKELY(type != 0))
196   {
197     DALI_LOG_ERROR("Unknown Format!\n");
198     return false;
199   }
200
201   if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
202   {
203     return false;
204   }
205
206   lineByteLength = (w + 7) >> 3;
207   // fsize was wrong! Load failed.
208   if(DALI_UNLIKELY(position + h * lineByteLength > fsize))
209   {
210     DALI_LOG_ERROR("Pixel infomation is bigger than file size! (%u + %u * %u > %u)\n", static_cast<std::uint32_t>(position), h, lineByteLength, fsize);
211     return false;
212   }
213
214   // w >= 1 and h >= 1. So we can assume that outputPixels is not null.
215   auto outputPixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
216
217   /**
218    * @code
219    * std::uint8_t* line = NULL;
220    * std::uint32_t cur  = 0, x, y;
221    * for(y = 0; y < h; y++)
222    * {
223    *   line = &map[0] + position;
224    *   position += lineByteLength;
225    *   for(x = 0; x < w; x++)
226    *   {
227    *     int idx    = x >> 3;
228    *     int offset = 1 << (0x07 - (x & 0x07));
229    *     if(line[idx] & offset)
230    *     {
231    *       outputPixels[cur] = 0xff; //0xffffffff;
232    *     }
233    *     else
234    *     {
235    *       outputPixels[cur] = 0x00; //0xff000000;
236    *     }
237    *     cur++;
238    *   }
239    * }
240    * @endcode
241    */
242
243   const std::uint8_t* inputPixels                  = inputBufferPtr + position;
244   const std::uint32_t lineByteLengthWithoutPadding = w >> 3;
245   const std::uint8_t  linePadding                  = w & 0x07;
246
247   for(std::uint32_t y = 0; y < h; ++y)
248   {
249     for(std::uint32_t x = 0; x < lineByteLengthWithoutPadding; ++x)
250     {
251       // memset whole 8 bits
252       // outputPixels filled 4 bytes in one operation.
253       // cachedCalculation4BitTo4ByteTable calculated in compile-time.
254       *(reinterpret_cast<std::uint32_t*>(outputPixels + 0)) = cachedCalculation4BitTo4ByteTable[((*inputPixels) >> 4) & 0x0f];
255       *(reinterpret_cast<std::uint32_t*>(outputPixels + 4)) = cachedCalculation4BitTo4ByteTable[(*inputPixels) & 0x0f];
256       outputPixels += 8;
257       ++inputPixels;
258     }
259     if(linePadding > 0)
260     {
261       // memset linePadding bits naive.
262       for(std::uint8_t x = 0; x < linePadding; ++x)
263       {
264         const std::uint8_t offset = (0x07 - (x & 0x07));
265         *outputPixels             = ((*inputPixels) >> offset) & 1 ? 0xff : 0x00;
266         ++outputPixels;
267       }
268       ++inputPixels;
269     }
270   }
271
272   return true;
273 }
274
275 bool LoadWbmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
276 {
277   FILE* const fp = input.file;
278   if(DALI_UNLIKELY(fp == NULL))
279   {
280     DALI_LOG_ERROR("Error loading bitmap\n");
281     return false;
282   }
283   Dali::Vector<unsigned char> map;
284   size_t                      position = 0;
285
286   unsigned int w, h;
287   unsigned int type;
288   if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
289   {
290     DALI_LOG_ERROR("Error seeking WBMP data\n");
291     return false;
292   }
293   long positionIndicator = ftell(fp);
294
295   unsigned int fsize(0u);
296   if(positionIndicator > -1L)
297   {
298     fsize = static_cast<unsigned int>(positionIndicator);
299   }
300
301   if(DALI_UNLIKELY(0u == fsize))
302   {
303     return false;
304   }
305
306   if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
307   {
308     DALI_LOG_ERROR("Error seeking WBMP data\n");
309     return false;
310   }
311   if(DALI_UNLIKELY(fsize <= 4))
312   {
313     DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
314     return false;
315   }
316
317   // type(1 byte) + fixedheader(1 byte) + width(uint) + height(uint)
318   unsigned int headerSize = 1 + 1 + 4 + 4; // 8 + 8 + 32 + 32;
319   headerSize              = std::min(headerSize, fsize);
320
321   map.ResizeUninitialized(headerSize);
322   if(DALI_UNLIKELY(fread(&map[0], 1, headerSize, fp) != headerSize))
323   {
324     DALI_LOG_WARNING("image file read opeation error!\n");
325     return false;
326   }
327
328   const std::uint8_t* const inputBufferPtr = &map[0];
329
330   if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, headerSize, &position) < 0))
331   {
332     DALI_LOG_ERROR("Error: unable to read type!\n");
333     return false;
334   }
335   position++; /* skipping one byte */
336   if(DALI_UNLIKELY(type != 0))
337   {
338     DALI_LOG_ERROR("Error: unknown format!\n");
339     return false;
340   }
341   if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, headerSize, &position) < 0))
342   {
343     DALI_LOG_ERROR("Error: can not read width!\n");
344     return false;
345   }
346   if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, headerSize, &position) < 0))
347   {
348     DALI_LOG_ERROR("Error: can not read height!\n");
349     return false;
350   }
351
352   if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
353   {
354     DALI_LOG_ERROR("Error: file size is not supported!\n");
355     return false;
356   }
357
358   width  = w;
359   height = h;
360   return true;
361 }
362
363 } // namespace TizenPlatform
364 } // namespace Dali