[dali_2.3.31] Merge branch 'devel/master'
[platform/core/uifw/dali-adaptor.git] / dali / internal / imaging / common / loader-wbmp.cpp
index 58c3c34..1f8f0c3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2023 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <cstring>
 
 // INTERNAL INCLUDES
-#include <dali/integration-api/debug.h>
 #include <dali/devel-api/adaptor-framework/pixel-buffer.h>
+#include <dali/integration-api/debug.h>
 
 namespace Dali
 {
-
 namespace TizenPlatform
 {
-
 namespace
 {
-
 #if defined(DEBUG_ENABLED)
 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_LOADER_WBMP");
 #endif
 
-#define IMG_MAX_SIZE 65536
+// TODO : We need to determine it in dali-common.h or something else. Currently, we set this value in code level.
+#define DALI_BYTE_ORDER_BIG_ENDIAN 0
 
-#define IMG_TOO_BIG(w, h) \
-        ((((unsigned long long)w) * ((unsigned long long)h)) >= \
-           ((1ULL << (29)) - 2048))
+#define IMG_MAX_SIZE 65536
 
+#define IMG_TOO_BIG(w, h)                                 \
+  ((((unsigned long long)w) * ((unsigned long long)h)) >= \
+   ((1ULL << (29)) - 2048))
 
 //extract multiple bytes integer , and saved in *data
-int extractMultiByteInteger(unsigned int *data, void *map, size_t length, size_t *position)
+int extractMultiByteInteger(uint32_t* data, const uint8_t* const& map, size_t length, size_t* position)
 {
   // the header field contains an image type indentifier of multi-byte length(TypeField), an octet of general header info(FixHeaderField)
   //,  a multi-byte width field(Width) and a multi-byte height field(Height) and so on.
   // The actual organisation of the image data depends on the image type
   // for Ext Headers flag (7th bit), 1 = More will follow, 0 = Last octet
   // so in the for loop, if(buf & 0x80 == 0), loop will be exited
-  int targetMultiByteInteger = 0, readBufCount;
-  unsigned char buf;
+  int     targetMultiByteInteger = 0, readBufCount;
+  uint8_t buf;
 
-  for (readBufCount = 0;;)
+  for(readBufCount = 0;;)
   {
     // readBufCount means the count that fetched data from map
     // extractMultiByteInteger() is to fetch wbmp type , width, and height
@@ -67,18 +66,18 @@ int extractMultiByteInteger(unsigned int *data, void *map, size_t length, size_t
     // for general width and height, if(buf & 0x80) == 0, then the next byte does not need to fetch again
     // first step, readBufCount = 1 , read int(4 bytes) to buf, if buf & 0x80 !=0, the buf need to continue to fetch
     // second step, readBufCount = 2, read next( 4 bytes) to buf, if buf & 0x80 == 0, then assigned the buf to target
-    if ((readBufCount ++) == 4)
+    if(DALI_UNLIKELY((readBufCount++) == 4))
     {
       return -1;
     }
-    if (*position > length)
+    if(DALI_UNLIKELY(*position > length))
     {
       return -1;
     }
-    buf = reinterpret_cast< unsigned char * >( map )[(*position)++];
+    buf                    = map[(*position)++];
     targetMultiByteInteger = (targetMultiByteInteger << 7) | (buf & 0x7f);
 
-    if ((buf & 0x80) == 0)
+    if((buf & 0x80) == 0)
     {
       DALI_LOG_INFO(gLogFilter, Debug::Verbose, "position: %d, readBufCount: %d\n", *position, readBufCount);
       break;
@@ -88,214 +87,246 @@ int extractMultiByteInteger(unsigned int *data, void *map, size_t length, size_t
   return 0;
 }
 
-}// end unnamed namespace
+// Calculate 4bit integer into 4byte integer
+constexpr uint32_t Calculate4BitTo4Byte(const uint8_t& input)
+{
+  uint32_t output = 0;
+#if DALI_BYTE_ORDER_BIG_ENDIAN
+  output |= static_cast<uint32_t>(input & 0x08) << 21;
+  output |= static_cast<uint32_t>(input & 0x04) << 14;
+  output |= static_cast<uint32_t>(input & 0x02) << 7;
+  output |= static_cast<uint32_t>(input & 0x01);
+#else
+  output |= static_cast<uint32_t>(input & 0x08) >> 3;
+  output |= static_cast<uint32_t>(input & 0x04) << 6;
+  output |= static_cast<uint32_t>(input & 0x02) << 15;
+  output |= static_cast<uint32_t>(input & 0x01) << 24;
+#endif
+  return output * 0xff;
+}
 
-bool LoadBitmapFromWbmp( const ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap )
+/**
+ * @brief Calculation result bit-->byte table in compile.
+ * Required memory = 16 * 4byte = 64byte
+ */
+// clang-format off
+constexpr std::uint32_t cachedCalculation4BitTo4ByteTable[16] = {
+  Calculate4BitTo4Byte(0x00), Calculate4BitTo4Byte(0x01), Calculate4BitTo4Byte(0x02), Calculate4BitTo4Byte(0x03),
+  Calculate4BitTo4Byte(0x04), Calculate4BitTo4Byte(0x05), Calculate4BitTo4Byte(0x06), Calculate4BitTo4Byte(0x07),
+  Calculate4BitTo4Byte(0x08), Calculate4BitTo4Byte(0x09), Calculate4BitTo4Byte(0x0a), Calculate4BitTo4Byte(0x0b),
+  Calculate4BitTo4Byte(0x0c), Calculate4BitTo4Byte(0x0d), Calculate4BitTo4Byte(0x0e), Calculate4BitTo4Byte(0x0f)};
+// clang-format on
+
+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)
 {
-  FILE* const fp = input.file;
-  if(fp == NULL)
+  if(DALI_UNLIKELY(fp == NULL))
   {
     DALI_LOG_ERROR("Error loading bitmap\n");
     return false;
   }
-  Dali::Vector<unsigned char> map;
-  Dali::Vector<unsigned char> surface;//unsigned int
-  size_t position = 0;
-
-  unsigned int w, h;
-  unsigned int type;
-  unsigned int line_length;
-  unsigned char *line = NULL;
-  unsigned int cur = 0, x, y;
+  position = 0;
 
-  if( fseek(fp,0,SEEK_END) )
+  uint32_t w, h;
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
   {
     DALI_LOG_ERROR("Error seeking WBMP data\n");
     return false;
   }
   long positionIndicator = ftell(fp);
 
-  unsigned int fsize( 0u );
-  if( positionIndicator > -1L )
+  fsize = 0u;
+  if(positionIndicator > -1L)
   {
-    fsize = static_cast<unsigned int>(positionIndicator);
+    fsize = static_cast<uint32_t>(positionIndicator);
   }
 
-  if( 0u == fsize )
+  if(DALI_UNLIKELY(0u == fsize))
   {
     DALI_LOG_ERROR("Error: filesize is 0!\n");
     return false;
   }
 
-  if( fseek(fp, 0, SEEK_SET) )
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
   {
     DALI_LOG_ERROR("Error seeking WBMP data\n");
     return false;
   }
-  if(fsize <= 4)
+  if(DALI_UNLIKELY(fsize <= 4))
   {
-    DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
+    DALI_LOG_ERROR("Error: WBMP Raw Data Not Found! Maybe this image is not wbmp format. fileSize : %u\n", fsize);
     return false;
   }
-  if(fsize > 4096 * 4096 * 4)
+  if(DALI_UNLIKELY(fsize > 4096 * 4096 * 4))
   {
-    DALI_LOG_ERROR("Error: WBMP size is too large!\n");
+    DALI_LOG_ERROR("Error: WBMP size is too large! fileSize : %u\n", fsize);
     return false;
   }
-  map.Resize(fsize);
 
-  if(fread(&map[0], 1, fsize, fp) != fsize)
+  // Read only 80 bytes if we are try to getting header file now.
+  // Else, read whole file data into map
+  uint32_t readDataSize = fsize;
+
+  if(loadHeaderOnly)
   {
-    DALI_LOG_WARNING("image file read opeation error!\n");
-    return false;
+    // type(1 byte) + fixedheader(1 byte) + width(uint) + height(uint)
+    uint32_t headerSize = 1 + 1 + 4 + 4; // 8 + 8 + 32 + 32;
+
+    readDataSize = std::min(headerSize, fsize);
   }
 
-  if (extractMultiByteInteger(&type, &map[0], fsize, &position) < 0)
+  map.ResizeUninitialized(fsize);
+  if(DALI_UNLIKELY(fread(&map[0], 1, readDataSize, fp) != readDataSize))
   {
+    DALI_LOG_WARNING("image file read opeation error! fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
     return false;
   }
 
-  position++; /* skipping one byte */
+  const std::uint8_t* const inputBufferPtr = &map[0];
 
-  if (extractMultiByteInteger(&w, &map[0], fsize, &position) < 0)
+  if(DALI_UNLIKELY(extractMultiByteInteger(&type, inputBufferPtr, readDataSize, &position) < 0))
   {
+    DALI_LOG_ERROR("Error: unable to read type! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
     return false;
   }
-  if (extractMultiByteInteger(&h, &map[0], fsize, &position) < 0)
+
+  position++; /* skipping one byte */
+
+  if(DALI_UNLIKELY(type != 0))
   {
+    DALI_LOG_ERROR("Error: unknown wbmp format! Maybe this image is not wbmp format. type : %u, fileSize : %u, readDataSize : %u\n", type, fsize, readDataSize);
     return false;
   }
-  if(type != 0)
+  if(DALI_UNLIKELY(extractMultiByteInteger(&w, inputBufferPtr, readDataSize, &position) < 0))
   {
-    DALI_LOG_ERROR("Unknown Format!\n");
+    DALI_LOG_ERROR("Error: can not read width! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
     return false;
   }
-
-  if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE))
+  if(DALI_UNLIKELY(extractMultiByteInteger(&h, inputBufferPtr, readDataSize, &position) < 0))
   {
+    DALI_LOG_ERROR("Error: can not read height! Maybe this image is not wbmp format. fileSize : %u, readDataSize : %u\n", fsize, readDataSize);
     return false;
   }
 
-  surface.Resize(w* h );//(w * h * 4);
-  memset(&surface[0], 0, w * h ); // w * h * 4
-
-  line_length = (w + 7) >> 3;
-  for (y = 0; y < h; y ++)
+  if(DALI_UNLIKELY((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE)))
   {
-    if (position + line_length > fsize)
-    {
-      return false;
-    }
-    line = &map[0] + position;
-    position += line_length;
-    for (x = 0; x < w; x++)
-    {
-      int idx = x >> 3;
-      int offset = 1 << (0x07 - (x & 0x07));
-      if (line[idx] & offset)
-      {
-        surface[cur] = 0xff;//0xffffffff;
-      }
-      else
-      {
-        surface[cur] = 0x00;//0xff000000;
-      }
-      cur++;
-    }
+    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);
+    return false;
   }
-  auto pixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
-
-  memcpy( pixels, &surface[0], w * h ); //w * h * 4
 
+  width  = w;
+  height = h;
   return true;
 }
+} // end unnamed namespace
 
-
-bool LoadWbmpHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
+bool LoadBitmapFromWbmp(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
 {
   FILE* const fp = input.file;
-  if(fp == NULL)
-  {
-    DALI_LOG_ERROR("Error loading bitmap\n");
-    return false;
-  }
-  Dali::Vector<unsigned char> map;
-  size_t position = 0;
 
-  unsigned int  w, h;
-  unsigned int type;
-  if( fseek(fp,0,SEEK_END) )
+  Dali::Vector<uint8_t> map;
+  uint32_t              fsize;
+  size_t                position = 0;
+  uint32_t              w, h;
+  uint32_t              type;
+  if(DALI_UNLIKELY(!LoadWbmpHeader(fp, w, h, type, fsize, map, position, false)))
   {
-    DALI_LOG_ERROR("Error seeking WBMP data\n");
+    DALI_LOG_ERROR("Error loading wbmp header\n");
     return false;
   }
-  long positionIndicator = ftell(fp);
 
-  unsigned int fsize( 0u );
-  if( positionIndicator > -1L )
-  {
-    fsize = static_cast<unsigned int>(positionIndicator);
-  }
-
-  if( 0u == fsize )
-  {
-    return false;
-  }
+  uint32_t lineByteLength = (w + 7) >> 3;
 
-  if( fseek(fp, 0, SEEK_SET) )
+  // fsize was wrong! Load failed.
+  if(DALI_UNLIKELY(position + h * lineByteLength > fsize))
   {
-    DALI_LOG_ERROR("Error seeking WBMP data\n");
+    DALI_LOG_ERROR("Pixel infomation is bigger than file size! (%u + %u * %u > %u)\n", static_cast<std::uint32_t>(position), h, lineByteLength, fsize);
     return false;
   }
-  if(fsize <= 4)
+  const uint8_t* const inputBufferPtr = &map[0];
+
+  // w >= 1 and h >= 1. So we can assume that outputPixels is not null.
+  auto outputPixels = (bitmap = Dali::Devel::PixelBuffer::New(w, h, Pixel::L8)).GetBuffer();
+
+  /**
+   * @code
+   * std::uint8_t* line = NULL;
+   * std::uint32_t cur  = 0, x, y;
+   * for(y = 0; y < h; y++)
+   * {
+   *   line = &map[0] + position;
+   *   position += lineByteLength;
+   *   for(x = 0; x < w; x++)
+   *   {
+   *     int idx    = x >> 3;
+   *     int offset = 1 << (0x07 - (x & 0x07));
+   *     if(line[idx] & offset)
+   *     {
+   *       outputPixels[cur] = 0xff; //0xffffffff;
+   *     }
+   *     else
+   *     {
+   *       outputPixels[cur] = 0x00; //0xff000000;
+   *     }
+   *     cur++;
+   *   }
+   * }
+   * @endcode
+   */
+
+  const uint8_t* inputPixels                 = inputBufferPtr + position;
+  const uint32_t lineBitLengthWithoutPadding = (w >> 3) << 3;
+
+  for(uint32_t y = 0; y < h; ++y)
   {
-    DALI_LOG_ERROR("Error: WBMP Raw Data Not Found!\n");
-    return false;
+    uint32_t x = 0;
+    if((reinterpret_cast<std::ptrdiff_t>(outputPixels) & (sizeof(uint32_t) - 1)) == 0)
+    {
+      for(; x < lineBitLengthWithoutPadding; x += 8)
+      {
+        // memset whole 8 bits
+        // outputPixels filled 4 bytes in one operation.
+        // cachedCalculation4BitTo4ByteTable calculated in compile-time.
+        *(reinterpret_cast<uint32_t*>(outputPixels + 0)) = cachedCalculation4BitTo4ByteTable[((*inputPixels) >> 4) & 0x0f];
+        *(reinterpret_cast<uint32_t*>(outputPixels + 4)) = cachedCalculation4BitTo4ByteTable[(*inputPixels) & 0x0f];
+        outputPixels += 8;
+        ++inputPixels;
+      }
+    }
+    {
+      // memset linePadding bits naive.
+      for(; x < w; ++x)
+      {
+        const uint8_t offset = (0x07 - (x & 0x07));
+        *outputPixels        = ((*inputPixels) >> offset) & 1 ? 0xff : 0x00;
+        ++outputPixels;
+        if(offset == 0)
+        {
+          ++inputPixels;
+        }
+      }
+    }
   }
 
-  // type(1 byte) + fixedheader(1 byte) + width(uint) + height(uint)
-  unsigned int headerSize = 1 + 1 + 4 + 4;// 8 + 8 + 32 + 32;
-  headerSize = std::min(headerSize, fsize);
+  return true;
+}
 
-  map.Resize(headerSize);
-  if(fread(&map[0], 1, headerSize, fp) != headerSize)
-  {
-    DALI_LOG_WARNING("image file read opeation error!\n");
-    return false;
-  }
+bool LoadWbmpHeader(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height)
+{
+  FILE* const fp = input.file;
 
-  if (extractMultiByteInteger(&type, &map[0], headerSize, &position) < 0)
-  {
-    DALI_LOG_ERROR("Error: unable to read type!\n");
-    return false;
-  }
-  position++; /* skipping one byte */
-  if(type != 0)
-  {
-    DALI_LOG_ERROR("Error: unknown format!\n");
-    return false;
-  }
-  if (extractMultiByteInteger(&w, &map[0], headerSize, &position) < 0)
-  {
-    DALI_LOG_ERROR("Error: can not read width!\n");
-    return false;
-  }
-  if (extractMultiByteInteger(&h, &map[0], headerSize, &position) < 0)
-  {
-    DALI_LOG_ERROR("Error: can not read height!\n");
-    return false;
-  }
+  Dali::Vector<uint8_t> map;
+  size_t                position = 0;
+  uint32_t              type;
+  uint32_t              fsize;
 
-  if ((w < 1) || (h < 1) || (w > IMG_MAX_SIZE) || (h > IMG_MAX_SIZE) )
+  if(DALI_UNLIKELY(!LoadWbmpHeader(fp, width, height, type, fsize, map, position, true)))
   {
-    DALI_LOG_ERROR("Error: file size is not supported!\n");
+    DALI_LOG_ERROR("Error loading wbmp header! Maybe this image is not wbmp format.\n");
     return false;
   }
 
-  width = w;
-  height = h;
   return true;
 }
 
-}
-}
+} // namespace TizenPlatform
+} // namespace Dali