[dali_2.3.25] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-wbmp.cpp
1 /*
2  * Copyright (c) 2023 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(uint32_t* data, const 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   uint8_t 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 uint32_t Calculate4BitTo4Byte(const uint8_t& input)
92 {
93   uint32_t output = 0;
94 #if DALI_BYTE_ORDER_BIG_ENDIAN
95   output |= static_cast<uint32_t>(input & 0x08) << 21;
96   output |= static_cast<uint32_t>(input & 0x04) << 14;
97   output |= static_cast<uint32_t>(input & 0x02) << 7;
98   output |= static_cast<uint32_t>(input & 0x01);
99 #else
100   output |= static_cast<uint32_t>(input & 0x08) >> 3;
101   output |= static_cast<uint32_t>(input & 0x04) << 6;
102   output |= static_cast<uint32_t>(input & 0x02) << 15;
103   output |= static_cast<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
120 bool LoadWbmpHeader(FILE* const fp, unsigned int& width, unsigned int& height, uint32_t& type, uint32_t& fsize, Dali::Vector<uint8_t>& map, size_t& position, bool loadHeaderOnly)
121 {
122   if(DALI_UNLIKELY(fp == NULL))
123   {
124     DALI_LOG_ERROR("Error loading bitmap\n");
125     return false;
126   }
127   position = 0;
128
129   uint32_t w, h;
130   if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
131   {
132     DALI_LOG_ERROR("Error seeking WBMP data\n");
133     return false;
134   }
135   long positionIndicator = ftell(fp);
136
137   fsize = 0u;
138   if(positionIndicator > -1L)
139   {
140     fsize = static_cast<uint32_t>(positionIndicator);
141   }
142
143   if(DALI_UNLIKELY(0u == fsize))
144   {
145     DALI_LOG_ERROR("Error: filesize is 0!\n");
146     return false;
147   }
148
149   if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
150   {
151     DALI_LOG_ERROR("Error seeking WBMP data\n");
152     return false;
153   }
154   if(DALI_UNLIKELY(fsize <= 4))
155   {
156     DALI_LOG_ERROR("Error: WBMP Raw Data Not Found! Maybe this image is not wbmp format. fileSize : %u\n", fsize);
157     return false;
158   }
159   if(DALI_UNLIKELY(fsize > 4096 * 4096 * 4))
160   {
161     DALI_LOG_ERROR("Error: WBMP size is too large! fileSize : %u\n", fsize);
162     return false;
163   }
164
165   // Read only 80 bytes if we are try to getting header file now.
166   // Else, read whole file data into map
167   uint32_t readDataSize = fsize;
168
169   if(loadHeaderOnly)
170   {
171     // type(1 byte) + fixedheader(1 byte) + width(uint) + height(uint)
172     uint32_t headerSize = 1 + 1 + 4 + 4; // 8 + 8 + 32 + 32;
173
174     readDataSize = std::min(headerSize, fsize);
175   }
176
177   map.ResizeUninitialized(fsize);
178   if(DALI_UNLIKELY(fread(&map[0], 1, readDataSize, fp) != readDataSize))
179   {
180     DALI_LOG_WARNING("image file read opeation error! fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
181     return false;
182   }
183
184   const std::uint8_t* const inputBufferPtr = &map[0];
185
186   if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, readDataSize, &position) < 0))
187   {
188     DALI_LOG_ERROR("Error: unable to read type! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
189     return false;
190   }
191
192   position++; /* skipping one byte */
193
194   if(DALI_UNLIKELY(type != 0))
195   {
196     DALI_LOG_ERROR("Error: unknown wbmp format! Maybe this image is not wbmp format. type : %u, fileSize : %u, readDataSize : %u\n", type, fsize, readDataSize);
197     return false;
198   }
199   if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, readDataSize, &position) < 0))
200   {
201     DALI_LOG_ERROR("Error: can not read width! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
202     return false;
203   }
204   if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, readDataSize, &position) < 0))
205   {
206     DALI_LOG_ERROR("Error: can not read height! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
207     return false;
208   }
209
210   if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
211   {
212     DALI_LOG_ERROR("Error: file size is not supported! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u, width : %u, height : %u\n", fsize, readDataSize, w, h);
213     return false;
214   }
215
216   width  = w;
217   height = h;
218   return true;
219 }
220 } // end unnamed namespace
221
222 bool LoadBitmapFromWbmp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
223 {
224   FILE* const fp = input.file;
225
226   Dali::Vector<uint8_t> map;
227   uint32_t              fsize;
228   size_t                position = 0;
229   uint32_t              w, h;
230   uint32_t              type;
231   if(DALI_UNLIKELY(!LoadWbmpHeader(fp, w, h, type, fsize, map, position, false)))
232   {
233     DALI_LOG_ERROR("Error loading wbmp header\n");
234     return false;
235   }
236
237   uint32_t lineByteLength = (w + 7) >> 3;
238
239   // fsize was wrong! Load failed.
240   if(DALI_UNLIKELY(position + h * lineByteLength > fsize))
241   {
242     DALI_LOG_ERROR("Pixel infomation is bigger than file size! (%u + %u * %u > %u)\n", static_cast<std::uint32_t>(position), h, lineByteLength, fsize);
243     return false;
244   }
245   const uint8_t* const inputBufferPtr = &map[0];
246
247   // w >= 1 and h >= 1. So we can assume that outputPixels is not null.
248   auto outputPixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
249
250   /**
251    * @code
252    * std::uint8_t* line = NULL;
253    * std::uint32_t cur  = 0, x, y;
254    * for(y = 0; y < h; y++)
255    * {
256    *   line = &map[0] + position;
257    *   position += lineByteLength;
258    *   for(x = 0; x < w; x++)
259    *   {
260    *     int idx    = x >> 3;
261    *     int offset = 1 << (0x07 - (x & 0x07));
262    *     if(line[idx] & offset)
263    *     {
264    *       outputPixels[cur] = 0xff; //0xffffffff;
265    *     }
266    *     else
267    *     {
268    *       outputPixels[cur] = 0x00; //0xff000000;
269    *     }
270    *     cur++;
271    *   }
272    * }
273    * @endcode
274    */
275
276   const uint8_t* inputPixels                 = inputBufferPtr + position;
277   const uint32_t lineBitLengthWithoutPadding = (w >> 3) << 3;
278
279   for(uint32_t y = 0; y < h; ++y)
280   {
281     uint32_t x = 0;
282     if((reinterpret_cast<std::ptrdiff_t>(outputPixels) & (sizeof(uint32_t) - 1)) == 0)
283     {
284       for(; x < lineBitLengthWithoutPadding; x += 8)
285       {
286         // memset whole 8 bits
287         // outputPixels filled 4 bytes in one operation.
288         // cachedCalculation4BitTo4ByteTable calculated in compile-time.
289         *(reinterpret_cast<uint32_t*>(outputPixels + 0)) = cachedCalculation4BitTo4ByteTable[((*inputPixels) >> 4) & 0x0f];
290         *(reinterpret_cast<uint32_t*>(outputPixels + 4)) = cachedCalculation4BitTo4ByteTable[(*inputPixels) & 0x0f];
291         outputPixels += 8;
292         ++inputPixels;
293       }
294     }
295     {
296       // memset linePadding bits naive.
297       for(; x < w; ++x)
298       {
299         const uint8_t offset = (0x07 - (x & 0x07));
300         *outputPixels        = ((*inputPixels) >> offset) & 1 ? 0xff : 0x00;
301         ++outputPixels;
302         if(offset == 0)
303         {
304           ++inputPixels;
305         }
306       }
307     }
308   }
309
310   return true;
311 }
312
313 bool LoadWbmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
314 {
315   FILE* const fp = input.file;
316
317   Dali::Vector<uint8_t> map;
318   size_t                position = 0;
319   uint32_t              type;
320   uint32_t              fsize;
321
322   if(DALI_UNLIKELY(!LoadWbmpHeader(fp, width, height, type, fsize, map, position, true)))
323   {
324     DALI_LOG_ERROR("Error loading wbmp header! Maybe this image is not wbmp format.\n");
325     return false;
326   }
327
328   return true;
329 }
330
331 } // namespace TizenPlatform
332 } // namespace Dali