Speedup image channel operation + alpha masking + etc 02/271702/10
authorEunki, Hong <eunkiki.hong@samsung.com>
Fri, 25 Feb 2022 06:08:15 +0000 (15:08 +0900)
committerEunki, Hong <eunkiki.hong@samsung.com>
Mon, 28 Feb 2022 08:57:37 +0000 (17:57 +0900)
Refactoring image channel selection algorithm.
Previous version run some unneccessary iterations
to select the offset of channel.
This patch change that we use offset pre-calculated table.
Now we can select & apply O(1) time complexity.
+
Remove unneccessary float operation during alpha masking.
+
Add some more dark magics on linear interpolation jobs.
+
Add some more dark magics on scanline.
Before patch reduced only each-line. This patch also reduce between-line operation
This patch, we can calculate 8 components in one operation.

Change-Id: I3b1ce7abbd5d719cd580c789167970e9012e3dbd
Signed-off-by: Eunki, Hong <eunkiki.hong@samsung.com>
automated-tests/src/dali-adaptor-internal/utc-Dali-ImageOperations.cpp
dali/internal/imaging/common/alpha-mask.cpp
dali/internal/imaging/common/alpha-mask.h
dali/internal/imaging/common/image-operations.cpp
dali/internal/imaging/common/image-operations.h
dali/internal/imaging/common/pixel-buffer-impl.cpp
dali/internal/imaging/common/pixel-manipulation.cpp

index 17b7c0696f9d6afd5ff08c810e21227c316e207c..e7414ff460727e6862ab5d7993c2e4d8d428e60d 100644 (file)
@@ -947,6 +947,26 @@ int UtcDaliImageOperationsHalveScanlineInPlace1Byte(void)
   END_TEST;
 }
 
+/**
+ * @brief Test the function for averaging vertically-adjacent pairs of single-byte-per-pixel pixels on a scanline.
+ */
+int UtcDaliImageOperationsAverageScanlines1ExceptTest(void)
+{
+  // Edge cases for averagescanlines1:
+  unsigned char shortEven1[]   = {0x00, 0x00, 0xff, 0xff, 0xff, 0xfe, 0x01, 0x01, 0x01, 0x00, 0x01, 0x00, 0x02, 0x03, 0x00, 0x01};
+  unsigned char shortEven2[]   = {0x00, 0xff, 0x00, 0xff, 0x01, 0x01, 0xff, 0xfe, 0x00, 0x01, 0x01, 0x02, 0x00, 0x00, 0x03, 0x02};
+  unsigned char expectBuffer[] = {0x00, 0x7f, 0x7f, 0xff, 0x80, 0x7f, 0x80, 0x7f, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+  unsigned char outputBuffer[sizeof(shortEven1)];
+
+  AverageScanlines1(shortEven1, shortEven2, outputBuffer, sizeof(shortEven1));
+  for(unsigned i = 0; i < sizeof(shortEven1); ++i)
+  {
+    DALI_TEST_EQUALS(unsigned(outputBuffer[i]), unsigned(expectBuffer[i]), TEST_LOCATION);
+  }
+
+  END_TEST;
+}
+
 /**
  * @brief Test the function for averaging vertically-adjacent pairs of single-byte-per-pixel pixels on a scanline.
  */
@@ -964,19 +984,55 @@ int UtcDaliImageOperationsAverageScanlines1(void)
   }
 
   // Longer test reusing RGBA setup/test logic:
-  const size_t           scanlineLength = 4096u;
-  Dali::Vector<uint32_t> scanline1;
-  Dali::Vector<uint32_t> scanline2;
-  Dali::Vector<uint32_t> reference;
-  Dali::Vector<uint32_t> output;
-  SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+  {
+    const size_t           scanlineLength = 4096u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+  // Longer test reusing RGBA setup/test logic with none-8-divisable length
+  {
+    const size_t           scanlineLength = 1003u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  // Check the output matches the independently generated reference:
-  size_t numMatches = 0;
-  MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
-  DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  // Very short test reusing RGBA setup/test logic with less-than-8 length
+  {
+    const size_t           scanlineLength = 1003u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines1((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
   END_TEST;
 }
@@ -999,19 +1055,55 @@ int UtcDaliImageOperationsAverageScanlines2(void)
   }
 
   // Longer test reusing RGBA setup/test logic:
-  const size_t           scanlineLength = 4096u;
-  Dali::Vector<uint32_t> scanline1;
-  Dali::Vector<uint32_t> scanline2;
-  Dali::Vector<uint32_t> reference;
-  Dali::Vector<uint32_t> output;
-  SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+  {
+    const size_t           scanlineLength = 4096u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+  // Longer test reusing RGBA setup/test logic with none-8-divisable length
+  {
+    const size_t           scanlineLength = 501u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  // Check the output matches the independently generated reference:
-  size_t numMatches = 0;
-  MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
-  DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  // Very short test reusing RGBA setup/test logic with less-than-8 length
+  {
+    const size_t           scanlineLength = 3u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines2((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 2);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
   END_TEST;
 }
@@ -1033,19 +1125,55 @@ int UtcDaliImageOperationsAverageScanlines3(void)
   }
 
   // Longer test reusing RGBA setup/test logic:
-  const size_t           scanlineLength = 3 * 4 * 90u;
-  Dali::Vector<uint32_t> scanline1;
-  Dali::Vector<uint32_t> scanline2;
-  Dali::Vector<uint32_t> reference;
-  Dali::Vector<uint32_t> output;
-  SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+  {
+    const size_t           scanlineLength = 3 * 4 * 90u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+  // Longer test reusing RGBA setup/test logic with none-8-divisable length
+  {
+    const size_t           scanlineLength = 3 * 501u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
-  // Check the output matches the independently generated reference:
-  size_t numMatches = 0;
-  MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
-  DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  // Very short test reusing RGBA setup/test logic with less-than-8 length
+  {
+    const size_t           scanlineLength = 3u;
+    Dali::Vector<uint32_t> scanline1;
+    Dali::Vector<uint32_t> scanline2;
+    Dali::Vector<uint32_t> reference;
+    Dali::Vector<uint32_t> output;
+    SetupScanlinesRGBA8888(scanlineLength, scanline1, scanline2, reference, output);
+
+    AverageScanlines3((const unsigned char*)&scanline1[0], (const unsigned char*)&scanline2[0], (unsigned char*)&output[0], scanlineLength * 4 / 3);
+
+    // Check the output matches the independently generated reference:
+    size_t numMatches = 0;
+    MatchScanlinesRGBA8888(reference, output, numMatches, TEST_LOCATION);
+    DALI_TEST_EQUALS(numMatches, reference.Count(), TEST_LOCATION);
+  }
 
   END_TEST;
 }
index d7f701635711391e913035f72b6ba09f19286389..5205da54136d8543f09117b6bebbf41c479a8881 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -55,30 +55,47 @@ void ApplyMaskToAlphaChannel(PixelBuffer& buffer, const PixelBuffer& mask)
   int srcOffset  = 0;
   int destOffset = 0;
 
-  float srcAlphaValue = 1.0f;
-
   // if image is premultiplied, the other channels of the image need to multiply by alpha.
   if(buffer.IsAlphaPreMultiplied())
   {
-    for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
+    // Collect all valid channel list before lookup whole buffer
+    std::vector<Channel> validChannelList;
+    for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE, Adaptor::ALPHA})
     {
-      for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
+      if(HasChannel(destPixelFormat, channel))
       {
-        auto srcAlpha      = ReadChannel(srcBuffer + srcOffset, srcPixelFormat, Adaptor::ALPHA);
-        auto destRed       = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::RED);
-        auto destGreen     = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::GREEN);
-        auto destBlue      = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::BLUE);
-        auto destLuminance = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::LUMINANCE);
-        auto destAlpha     = ReadChannel(destBuffer + destOffset, destPixelFormat, Adaptor::ALPHA);
-
-        WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::RED, destRed * srcAlpha / 255);
-        WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::GREEN, destGreen * srcAlpha / 255);
-        WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::BLUE, destBlue * srcAlpha / 255);
-        WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::LUMINANCE, destLuminance * srcAlpha / 255);
-        WriteChannel(destBuffer + destOffset, destPixelFormat, Adaptor::ALPHA, destAlpha * srcAlpha / 255);
-
-        srcOffset += srcBytesPerPixel;
-        destOffset += destBytesPerPixel;
+        validChannelList.emplace_back(channel);
+      }
+    }
+    if(DALI_LIKELY(!validChannelList.empty()))
+    {
+      for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
+      {
+        for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
+        {
+          auto srcAlpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
+          if(srcAlpha < 255)
+          {
+            // If alpha is 255, we don't need to change color. Skip current pixel
+            // But if alpha is not 255, we should change color.
+            if(srcAlpha > 0)
+            {
+              for(const Channel& channel : validChannelList)
+              {
+                auto color = ReadChannel(destBuffer + destOffset, destPixelFormat, channel);
+                WriteChannel(destBuffer + destOffset, destPixelFormat, channel, color * srcAlpha / 255);
+              }
+            }
+            else
+            {
+              // If alpha is 0, just set all pixel as zero.
+              memset(destBuffer + destOffset, 0, destBytesPerPixel);
+            }
+          }
+
+          srcOffset += srcBytesPerPixel;
+          destOffset += destBytesPerPixel;
+        }
       }
     }
   }
@@ -88,12 +105,11 @@ void ApplyMaskToAlphaChannel(PixelBuffer& buffer, const PixelBuffer& mask)
     {
       for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
       {
-        unsigned char alpha = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
-        srcAlphaValue       = float(alpha) / 255.0f;
+        unsigned char srcAlpha  = srcBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask;
+        unsigned char destAlpha = destBuffer[destOffset + destAlphaByteOffset] & destAlphaMask;
+
+        destAlpha = (static_cast<std::uint16_t>(destAlpha) * static_cast<std::uint16_t>(srcAlpha)) / 255;
 
-        unsigned char destAlpha      = destBuffer[destOffset + destAlphaByteOffset] & destAlphaMask;
-        float         destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f);
-        destAlpha                    = destAlphaValue;
         destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask;
         destBuffer[destOffset + destAlphaByteOffset] |= (destAlpha & destAlphaMask);
 
@@ -143,27 +159,24 @@ PixelBufferPtr CreateNewMaskedBuffer(const PixelBuffer& buffer, const PixelBuffe
   int  destOffset     = 0;
   bool hasAlpha       = Dali::Pixel::HasAlpha(buffer.GetPixelFormat());
 
-  float         srcAlphaValue = 1.0f;
-  unsigned char destAlpha     = 0;
+  unsigned char destAlpha = 0;
 
   for(unsigned int row = 0; row < buffer.GetHeight(); ++row)
   {
     for(unsigned int col = 0; col < buffer.GetWidth(); ++col)
     {
-      unsigned char alpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
-      srcAlphaValue       = float(alpha) / 255.0f;
+      unsigned char srcAlpha = srcBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask;
 
       ConvertColorChannelsToRGBA8888(oldBuffer, srcColorOffset, srcColorPixelFormat, destBuffer, destOffset);
 
       if(hasAlpha)
       {
-        destAlpha            = ConvertAlphaChannelToA8(oldBuffer, srcColorOffset, srcColorPixelFormat);
-        float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f);
-        destAlpha            = destAlphaValue;
+        destAlpha = ConvertAlphaChannelToA8(oldBuffer, srcColorOffset, srcColorPixelFormat);
+        destAlpha = (static_cast<std::uint16_t>(destAlpha) * static_cast<std::uint16_t>(srcAlpha)) / 255;
       }
       else
       {
-        destAlpha = floorf(Clamp(srcAlphaValue * 255.0f, 0.0f, 255.0f));
+        destAlpha = srcAlpha;
       }
 
       destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask;
index 44f8c0352bb4089b89fd540d4b0a22594bc3f9d8..8caaca535d457cfd2b5d72f7bd9bc3b53ab1cef8 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_INTERNAL_ADAPTOR_ALPHA_MASK_H
 
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -25,17 +25,19 @@ namespace Internal
 namespace Adaptor
 {
 /**
- * Apply the mask to a buffer's alpha channel
+ * @brief Apply the mask to a buffer's alpha channel
+ * @note It works well only if mask's alpha mask is 8bit
  * @param[in] buffer The buffer to apply the mask to
  * @param[in] mask The mask to apply
  */
 void ApplyMaskToAlphaChannel(PixelBuffer& buffer, const PixelBuffer& mask);
 
 /**
- * Create a new PixelBuffer with an alpha channel large enough to handle the alpha from
+ * @brief Create a new PixelBuffer with an alpha channel large enough to handle the alpha from
  * the mask, converting the color values to the new size, and either multiplying the mask's
  * alpha into the existing alpha value, or writing the mask's alpha value directly into
  * the new buffer's alpha channel.
+ * @note It works well only if mask's alpha mask is 8bit
  *
  * @param[in] buffer The buffer to apply the mask to
  * @param[in] mask The mask to apply
index d0c7f79a17dd8cbb3d95dfd78b7d4bf2a2e560c4..772d26f712b0f214832d6262f2ddf08ee88269de 100644 (file)
@@ -1367,10 +1367,51 @@ void HalveScanlineInPlace1Byte(unsigned char* const pixels, const unsigned int w
   }
 }
 
+// AverageScanline
+
+namespace
+{
 /**
- * @ToDo: Optimise for ARM using a 4 bytes at a time loop wrapped around the single ARMV6 instruction: UHADD8  R4, R0, R5. Note, this is not neon. It runs in the normal integer pipeline so there is no downside like a stall moving between integer and copro, or extra power for clocking-up the idle copro.
- * if (widthInComponents >= 7) { word32* aligned1 = scanline1 + 3 & 3; word32* aligned1_end = scanline1 + widthInPixels & 3; while(aligned1 < aligned1_end) { UHADD8  *aligned1++, *aligned2++, *alignedoutput++ } .. + 0 to 3 spare pixels at each end.
+ * @copydoc AverageScanlines1
+ * @note This API average eight components in one operation.
+ * It will give performance benifit.
  */
+inline void AverageScanlinesWithEightComponents(
+  const unsigned char* const scanline1,
+  const unsigned char* const __restrict__ scanline2,
+  unsigned char* const outputScanline,
+  const unsigned int   totalComponentCount)
+{
+  unsigned int component = 0;
+  if(DALI_LIKELY(totalComponentCount >= 8))
+  {
+    // Jump 8 components in one step
+    const std::uint64_t* const scanline18Step = reinterpret_cast<const std::uint64_t* const>(scanline1);
+    const std::uint64_t* const scanline28Step = reinterpret_cast<const std::uint64_t* const>(scanline2);
+    std::uint64_t* const       output8step    = reinterpret_cast<std::uint64_t* const>(outputScanline);
+
+    const std::uint32_t totalStepCount = (totalComponentCount) >> 3;
+    component                          = totalStepCount << 3;
+
+    // and for each step, calculate average of 8 bytes.
+    for(std::uint32_t i = 0; i < totalStepCount; ++i)
+    {
+      const auto& c1     = *(scanline18Step + i);
+      const auto& c2     = *(scanline28Step + i);
+      *(output8step + i) = static_cast<std::uint64_t>((((c1 ^ c2) & 0xfefefefefefefefeull) >> 1) + (c1 & c2));
+    }
+  }
+  // remaining components calculate
+  for(; component < totalComponentCount; ++component)
+  {
+    const auto& c1            = scanline1[component];
+    const auto& c2            = scanline2[component];
+    outputScanline[component] = static_cast<std::uint8_t>(((c1 ^ c2) >> 1) + (c1 & c2));
+  }
+}
+
+} // namespace
+
 void AverageScanlines1(const unsigned char* const scanline1,
                        const unsigned char* const __restrict__ scanline2,
                        unsigned char* const outputScanline,
@@ -1378,10 +1419,15 @@ void AverageScanlines1(const unsigned char* const scanline1,
 {
   DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width);
 
-  for(unsigned int component = 0; component < width; ++component)
-  {
-    outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
-  }
+  /**
+   * @code
+   * for(unsigned int component = 0; component < width; ++component)
+   * {
+   *   outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+   * }
+   * @endcode
+   */
+  AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width);
 }
 
 void AverageScanlines2(const unsigned char* const scanline1,
@@ -1391,10 +1437,15 @@ void AverageScanlines2(const unsigned char* const scanline1,
 {
   DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width * 2);
 
-  for(unsigned int component = 0; component < width * 2; ++component)
-  {
-    outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
-  }
+  /**
+   * @code
+   * for(unsigned int component = 0; component < width * 2; ++component)
+   * {
+   *   outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+   * }
+   * @endcode
+   */
+  AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width * 2);
 }
 
 void AverageScanlines3(const unsigned char* const scanline1,
@@ -1404,10 +1455,15 @@ void AverageScanlines3(const unsigned char* const scanline1,
 {
   DebugAssertDualScanlineParameters(scanline1, scanline2, outputScanline, width * 3);
 
-  for(unsigned int component = 0; component < width * 3; ++component)
-  {
-    outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
-  }
+  /**
+   * @code
+   * for(unsigned int component = 0; component < width * 3; ++component)
+   * {
+   *   outputScanline[component] = static_cast<unsigned char>(AverageComponent(scanline1[component], scanline2[component]));
+   * }
+   * @endcode
+   */
+  AverageScanlinesWithEightComponents(scanline1, scanline2, outputScanline, width * 3);
 }
 
 void AverageScanlinesRGBA8888(const unsigned char* const scanline1,
@@ -1472,29 +1528,38 @@ void DownscaleInPlacePow2(unsigned char* const pixels,
     {
       const BoxDimensionTest dimensionTest = DimensionTestForScalingMode(fittingMode);
 
-      if(pixelFormat == Pixel::RGBA8888)
-      {
-        Internal::Platform::DownscaleInPlacePow2RGBA8888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
-      }
-      else if(pixelFormat == Pixel::RGB888)
-      {
-        Internal::Platform::DownscaleInPlacePow2RGB888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
-      }
-      else if(pixelFormat == Pixel::RGB565)
-      {
-        Internal::Platform::DownscaleInPlacePow2RGB565(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
-      }
-      else if(pixelFormat == Pixel::LA88)
-      {
-        Internal::Platform::DownscaleInPlacePow2ComponentPair(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
-      }
-      else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
+      switch(pixelFormat)
       {
-        Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
-      }
-      else
-      {
-        DALI_ASSERT_DEBUG(false && "Inner branch conditions don't match outer branch.");
+        case Pixel::RGBA8888:
+        {
+          Internal::Platform::DownscaleInPlacePow2RGBA8888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+          break;
+        }
+        case Pixel::RGB888:
+        {
+          Internal::Platform::DownscaleInPlacePow2RGB888(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+          break;
+        }
+        case Pixel::RGB565:
+        {
+          Internal::Platform::DownscaleInPlacePow2RGB565(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+          break;
+        }
+        case Pixel::LA88:
+        {
+          Internal::Platform::DownscaleInPlacePow2ComponentPair(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+          break;
+        }
+        case Pixel::L8:
+        case Pixel::A8:
+        {
+          Internal::Platform::DownscaleInPlacePow2SingleBytePerPixel(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
+          break;
+        }
+        default:
+        {
+          DALI_ASSERT_DEBUG(false && "Inner branch conditions don't match outer branch.");
+        }
       }
     }
   }
@@ -1570,6 +1635,8 @@ void DownscaleInPlacePow2SingleBytePerPixel(unsigned char*   pixels,
   DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
 }
 
+// Point sampling group below
+
 namespace
 {
 /**
@@ -1731,25 +1798,34 @@ void PointSample(const unsigned char* inPixels,
   // Check the pixel format is one that is supported:
   if(pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
   {
-    if(pixelFormat == Pixel::RGB888)
+    switch(pixelFormat)
     {
-      PointSample3BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
-    }
-    else if(pixelFormat == Pixel::RGBA8888)
-    {
-      PointSample4BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
-    }
-    else if(pixelFormat == Pixel::RGB565 || pixelFormat == Pixel::LA88)
-    {
-      PointSample2BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
-    }
-    else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
-    {
-      PointSample1BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
-    }
-    else
-    {
-      DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+      case Pixel::RGB888:
+      {
+        PointSample3BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+        break;
+      }
+      case Pixel::RGBA8888:
+      {
+        PointSample4BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+        break;
+      }
+      case Pixel::RGB565:
+      case Pixel::LA88:
+      {
+        PointSample2BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+        break;
+      }
+      case Pixel::L8:
+      case Pixel::A8:
+      {
+        PointSample1BPP(inPixels, inputWidth, inputHeight, outPixels, desiredWidth, desiredHeight);
+        break;
+      }
+      default:
+      {
+        DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+      }
     }
   }
   else
@@ -2134,29 +2210,38 @@ void LinearSample(const unsigned char* __restrict__ inPixels,
   // Check the pixel format is one that is supported:
   if(pixelFormat == Pixel::RGB888 || pixelFormat == Pixel::RGBA8888 || pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8 || pixelFormat == Pixel::LA88 || pixelFormat == Pixel::RGB565)
   {
-    if(pixelFormat == Pixel::RGB888)
-    {
-      LinearSample3BPP(inPixels, inDimensions, outPixels, outDimensions);
-    }
-    else if(pixelFormat == Pixel::RGBA8888)
-    {
-      LinearSample4BPP(inPixels, inDimensions, outPixels, outDimensions);
-    }
-    else if(pixelFormat == Pixel::L8 || pixelFormat == Pixel::A8)
-    {
-      LinearSample1BPP(inPixels, inDimensions, outPixels, outDimensions);
-    }
-    else if(pixelFormat == Pixel::LA88)
+    switch(pixelFormat)
     {
-      LinearSample2BPP(inPixels, inDimensions, outPixels, outDimensions);
-    }
-    else if(pixelFormat == Pixel::RGB565)
-    {
-      LinearSampleRGB565(inPixels, inDimensions, outPixels, outDimensions);
-    }
-    else
-    {
-      DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+      case Pixel::RGB888:
+      {
+        LinearSample3BPP(inPixels, inDimensions, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::RGBA8888:
+      {
+        LinearSample4BPP(inPixels, inDimensions, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::L8:
+      case Pixel::A8:
+      {
+        LinearSample1BPP(inPixels, inDimensions, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::LA88:
+      {
+        LinearSample2BPP(inPixels, inDimensions, outPixels, outDimensions);
+        break;
+      }
+      case Pixel::RGB565:
+      {
+        LinearSampleRGB565(inPixels, inDimensions, outPixels, outDimensions);
+        break;
+      }
+      default:
+      {
+        DALI_ASSERT_DEBUG(0 == "Inner branch conditions don't match outer branch.");
+      }
     }
   }
   else
index dd8db98e24436cb2688efadfb6ce8f6ed1ad6051..d1105adabe0929610ec2fbc2a120771fbf65e959 100644 (file)
@@ -580,9 +580,14 @@ inline uint32_t AveragePixelRGB565(uint32_t a, uint32_t b)
 inline unsigned int WeightedBlendIntToFixed1616(unsigned int a, unsigned int b, unsigned int fractBlend)
 {
   DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
-  const unsigned int weightedAFixed = a * (65535u - fractBlend);
-  const unsigned int weightedBFixed = b * fractBlend;
-  const unsigned     blended        = (weightedAFixed + weightedBFixed);
+  /**
+   * @code
+   * const unsigned int weightedAFixed = a * (65535u - fractBlend);
+   * const unsigned int weightedBFixed = b * fractBlend;
+   * const unsigned     blended        = (weightedAFixed + weightedBFixed);
+   * @endcode
+   */
+  const unsigned int blended = (a << 16) - a + (static_cast<int32_t>(b) - static_cast<int32_t>(a)) * fractBlend;
   return blended;
 }
 
@@ -590,10 +595,15 @@ inline unsigned int WeightedBlendIntToFixed1616(unsigned int a, unsigned int b,
 inline uint64_t WeightedBlendFixed1616ToFixed1632(unsigned int a, unsigned int b, unsigned int fractBlend)
 {
   DALI_ASSERT_DEBUG(fractBlend <= 65535u && "Factor should be in 0.16 fixed-point.");
-  // Blend while promoting intermediates to 16.32 fixed point:
-  const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
-  const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
-  const uint64_t blended        = (weightedAFixed + weightedBFixed);
+  /**
+   * @code
+   * // Blend while promoting intermediates to 16.32 fixed point:
+   * const uint64_t weightedAFixed = uint64_t(a) * (65535u - fractBlend);
+   * const uint64_t weightedBFixed = uint64_t(b) * fractBlend;
+   * const uint64_t blended        = (weightedAFixed + weightedBFixed);
+   * @endcode
+   */
+  const uint64_t blended = (static_cast<uint64_t>(a) << 16) - a + (static_cast<int64_t>(b) - static_cast<int64_t>(a)) * fractBlend;
   return blended;
 }
 
@@ -605,9 +615,44 @@ inline unsigned int BilinearFilter1Component(unsigned int tl, unsigned int tr, u
   DALI_ASSERT_DEBUG(fractBlendHorizontal <= 65535u && "Factor should be in 0.16 fixed-point.");
   DALI_ASSERT_DEBUG(fractBlendVertical <= 65535u && "Factor should be in 0.16 fixed-point.");
 
-  const unsigned int topBlend   = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
-  const unsigned int botBlend   = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
-  const uint64_t     blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
+  /**
+   * @code
+   * const unsigned int topBlend   = WeightedBlendIntToFixed1616(tl, tr, fractBlendHorizontal);
+   * const unsigned int botBlend   = WeightedBlendIntToFixed1616(bl, br, fractBlendHorizontal);
+   * const uint64_t     blended2x2 = WeightedBlendFixed1616ToFixed1632(topBlend, botBlend, fractBlendVertical);
+   * const unsigned int rounded    = (blended2x2 + (1u << 31u)) >> 32u;
+   * @endcode
+   */
+
+  /**
+   * Hard-coding optimize!
+   *
+   * Let p = 65536, s.t we can optimze it as << 16.
+   * Let x = fractBlendHorizontal, y = fractBlendVertical.
+   * topBlend = (tl*p - tl - tl*x + tr*x)
+   * botBlend = (bl*p - bl - bl*x + br*x)
+   * blended2x2 = topBlend*p - topBlend - topBlend*y + botBlend*y
+   *
+   * And now we can split all values.
+   * tl*p*p - tl*p - tl*x*p + tr*x*p  -  tl*p + tl + tl*x - tr*x  -  tl*y*p + tl*y + tl*x*y - tr*x*y  +  bl*y*p - bl*y - bl*x*y + br*x*y;
+   * --> (collect by p, x, and y)
+   * (tl)*p*p + (-2tl + (-tl + tr)*x + (-tl+bl)*y)*p + tl + (tl - tr)*x + (tl - bl)*y + (tl - tr - bl + br)*x*y
+   *
+   * A = (tl - tr) * x;
+   * B = (tl - bl) * y;
+   * C = (tl - tr - bl + br) * x * y;
+   * D = (2*tl + A + B)
+   * -->
+   * (tl << 32) - (D << 16) + tl + A + B + C
+   *
+   * Becareful of overflow and negative value.
+   */
+  const int32_t A = (static_cast<int32_t>(tl) - static_cast<int32_t>(tr)) * static_cast<int32_t>(fractBlendHorizontal);
+  const int32_t B = (static_cast<int32_t>(tl) - static_cast<int32_t>(bl)) * static_cast<int32_t>(fractBlendVertical);
+  const int64_t C = (static_cast<int64_t>(tl) - static_cast<int64_t>(tr) - static_cast<int64_t>(bl) + static_cast<int64_t>(br)) * static_cast<int64_t>(fractBlendHorizontal) * static_cast<int64_t>(fractBlendVertical);
+  const int64_t D = ((static_cast<int64_t>(tl) << 1) + A + B);
+
+  const uint64_t     blended2x2 = (static_cast<int64_t>(tl) << 32u) - (D << 16u) + tl + A + B + C;
   const unsigned int rounded    = (blended2x2 + (1u << 31u)) >> 32u;
   return rounded;
 }
index 747dcb38c8928b905a4c764bb2ee6b95720a8029..4ff2250a5adf92ca75e2402ccb8d367d1f2ee0ec 100644 (file)
@@ -446,31 +446,41 @@ void PixelBuffer::MultiplyColorByAlpha()
     unsigned char*     pixel      = mBuffer;
     const unsigned int bufferSize = mWidth * mHeight;
 
-    for(unsigned int i = 0; i < bufferSize; ++i)
+    // Collect all valid channel list before lookup whole buffer
+    std::vector<Channel> validChannelList;
+    for(const Channel& channel : {Adaptor::RED, Adaptor::GREEN, Adaptor::BLUE, Adaptor::LUMINANCE})
     {
-      unsigned int alpha = ReadChannel(pixel, mPixelFormat, Adaptor::ALPHA);
-      if(alpha < 255)
+      if(HasChannel(mPixelFormat, channel))
       {
-        // If alpha is 255, we don't need to change color. Skip current pixel
-        // But if alpha is not 255, we should change color.
-        if(alpha > 0)
-        {
-          auto red       = ReadChannel(pixel, mPixelFormat, Adaptor::RED);
-          auto green     = ReadChannel(pixel, mPixelFormat, Adaptor::GREEN);
-          auto blue      = ReadChannel(pixel, mPixelFormat, Adaptor::BLUE);
-          auto luminance = ReadChannel(pixel, mPixelFormat, Adaptor::LUMINANCE);
-          WriteChannel(pixel, mPixelFormat, Adaptor::RED, red * alpha / 255);
-          WriteChannel(pixel, mPixelFormat, Adaptor::GREEN, green * alpha / 255);
-          WriteChannel(pixel, mPixelFormat, Adaptor::BLUE, blue * alpha / 255);
-          WriteChannel(pixel, mPixelFormat, Adaptor::LUMINANCE, luminance * alpha / 255);
-        }
-        else
+        validChannelList.emplace_back(channel);
+      }
+    }
+
+    if(DALI_LIKELY(!validChannelList.empty()))
+    {
+      for(unsigned int i = 0; i < bufferSize; ++i)
+      {
+        unsigned int alpha = ReadChannel(pixel, mPixelFormat, Adaptor::ALPHA);
+        if(alpha < 255)
         {
-          // If alpha is 0, just set all pixel as zero.
-          memset(pixel, 0, bytesPerPixel);
+          // If alpha is 255, we don't need to change color. Skip current pixel
+          // But if alpha is not 255, we should change color.
+          if(alpha > 0)
+          {
+            for(const Channel& channel : validChannelList)
+            {
+              auto color = ReadChannel(pixel, mPixelFormat, channel);
+              WriteChannel(pixel, mPixelFormat, channel, color * alpha / 255);
+            }
+          }
+          else
+          {
+            // If alpha is 0, just set all pixel as zero.
+            memset(pixel, 0, bytesPerPixel);
+          }
         }
+        pixel += bytesPerPixel;
       }
-      pixel += bytesPerPixel;
     }
   }
   mPreMultiplied = true;
index e4c3fe0f0ef760256e99fdd6a24c4555da86fd6d..93418d3fa84646c27a62ce2116d03c302ded5399 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
@@ -29,13 +29,20 @@ namespace Adaptor
 {
 namespace
 {
-constexpr Channel ALPHA_CHANNEL_ONLY[]       = {ALPHA};
-constexpr Channel LUMINANCE_CHANNEL_ONLY[]   = {LUMINANCE};
-constexpr Channel LUMINANCE_ALPHA_CHANNELS[] = {LUMINANCE, ALPHA};
-constexpr Channel RGB_CHANNELS[]             = {RED, GREEN, BLUE};
-constexpr Channel BGR_CHANNELS[]             = {BLUE, GREEN, RED};
-constexpr Channel RGBA_CHANNELS[]            = {RED, GREEN, BLUE, ALPHA};
-constexpr Channel BGRA_CHANNELS[]            = {BLUE, GREEN, RED, ALPHA};
+// clang-format off
+/**
+ * @brief Pre-defined offset tables for each Channel.
+ * If invalid channel, return -1. else, return the offset bytes.
+ */
+//                                                                           | LUMINANCE | RED | GREEN | BLUE | ALPHA | DEPTH | STENCIL |
+constexpr std::int8_t ALPHA_ONLY_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]      = {        -1 ,  -1 ,    -1 ,   -1 ,     0 ,    -1 ,      -1 };
+constexpr std::int8_t LUMINANCE_ONLY_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]  = {         0 ,  -1 ,    -1 ,   -1 ,    -1 ,    -1 ,      -1 };
+constexpr std::int8_t LUMINANCE_ALPHA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS] = {         0 ,  -1 ,    -1 ,   -1 ,     1 ,    -1 ,      -1 };
+constexpr std::int8_t RGB_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]             = {        -1 ,   0 ,     1 ,    2 ,    -1 ,    -1 ,      -1 };
+constexpr std::int8_t BGR_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]             = {        -1 ,   2 ,     1 ,    0 ,    -1 ,    -1 ,      -1 };
+constexpr std::int8_t RGBA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]            = {        -1 ,   0 ,     1 ,    2 ,     3 ,    -1 ,      -1 };
+constexpr std::int8_t BGRA_OFFSET_TABLE[MAX_NUMBER_OF_CHANNELS]            = {        -1 ,   2 ,     1 ,    0 ,     3 ,    -1 ,      -1 };
+// clang-format on
 
 /**
  * @brief Template to Read from a buffer with pixel formats that have one byte per channel.
@@ -43,22 +50,15 @@ constexpr Channel BGRA_CHANNELS[]            = {BLUE, GREEN, RED, ALPHA};
  * @tparam NumberOfChannels The number of channels to check
  * @param pixelData The pixel data to retrieve the value from
  * @param channel The channel we're after
- * @param channels The array of channels in the pixel format
+ * @param offsetTable The array of offset bytes for each channels in the pixel format
  * @return The value of the required channel
  */
-template<size_t NumberOfChannels>
-unsigned int ReadChannel(unsigned char* pixelData, Channel channel, const Channel (&channels)[NumberOfChannels])
+unsigned int ReadChannelTable(unsigned char* pixelData, Channel channel, const std::int8_t (&offsetTable)[MAX_NUMBER_OF_CHANNELS])
 {
-  auto num    = 0u;
   auto retVal = 0u;
-  for(auto current : channels)
+  if(offsetTable[channel] >= 0)
   {
-    if(channel == current)
-    {
-      retVal = static_cast<unsigned int>(*(pixelData + num));
-      break;
-    }
-    ++num;
+    retVal = static_cast<unsigned int>(*(pixelData + offsetTable[channel]));
   }
   return retVal;
 }
@@ -70,20 +70,13 @@ unsigned int ReadChannel(unsigned char* pixelData, Channel channel, const Channe
  * @param pixelData The pixel data to write the value to
  * @param channel The channel we're after
  * @param channelValue The value of the channel to set
- * @param channels The array of channels in the pixel format
+ * @param offsetTable The array of offset bytes for each channels in the pixel format
  */
-template<size_t NumberOfChannels>
-void WriteChannel(unsigned char* pixelData, Channel channel, unsigned int channelValue, const Channel (&channels)[NumberOfChannels])
+void WriteChannelTable(unsigned char* pixelData, Channel channel, unsigned int channelValue, const std::int8_t (&offsetTable)[MAX_NUMBER_OF_CHANNELS])
 {
-  auto num = 0u;
-  for(auto current : channels)
+  if(offsetTable[channel] >= 0)
   {
-    if(channel == current)
-    {
-      *(pixelData + num) = static_cast<unsigned char>(channelValue & 0xFF);
-      break;
-    }
-    ++num;
+    *(pixelData + offsetTable[channel]) = static_cast<unsigned char>(channelValue & 0xFF);
   }
 }
 
@@ -412,15 +405,15 @@ unsigned int ReadChannel(unsigned char*      pixelData,
   {
     case Dali::Pixel::A8:
     {
-      return ReadChannel(pixelData, channel, ALPHA_CHANNEL_ONLY);
+      return ReadChannelTable(pixelData, channel, ALPHA_ONLY_OFFSET_TABLE);
     }
     case Dali::Pixel::L8:
     {
-      return ReadChannel(pixelData, channel, LUMINANCE_CHANNEL_ONLY);
+      return ReadChannelTable(pixelData, channel, LUMINANCE_ONLY_OFFSET_TABLE);
     }
     case Dali::Pixel::LA88:
     {
-      return ReadChannel(pixelData, channel, LUMINANCE_ALPHA_CHANNELS);
+      return ReadChannelTable(pixelData, channel, LUMINANCE_ALPHA_OFFSET_TABLE);
     }
     case Dali::Pixel::RGB565:
     {
@@ -435,22 +428,22 @@ unsigned int ReadChannel(unsigned char*      pixelData,
     case Dali::Pixel::RGB888:
     case Dali::Pixel::RGB8888:
     {
-      return ReadChannel(pixelData, channel, RGB_CHANNELS);
+      return ReadChannelTable(pixelData, channel, RGB_OFFSET_TABLE);
     }
 
     case Dali::Pixel::BGR8888:
     {
-      return ReadChannel(pixelData, channel, BGR_CHANNELS);
+      return ReadChannelTable(pixelData, channel, BGR_OFFSET_TABLE);
     }
 
     case Dali::Pixel::RGBA8888:
     {
-      return ReadChannel(pixelData, channel, RGBA_CHANNELS);
+      return ReadChannelTable(pixelData, channel, RGBA_OFFSET_TABLE);
     }
 
     case Dali::Pixel::BGRA8888:
     {
-      return ReadChannel(pixelData, channel, BGRA_CHANNELS);
+      return ReadChannelTable(pixelData, channel, BGRA_OFFSET_TABLE);
     }
 
     case Dali::Pixel::RGBA4444:
@@ -496,17 +489,17 @@ void WriteChannel(unsigned char*      pixelData,
   {
     case Dali::Pixel::A8:
     {
-      WriteChannel(pixelData, channel, channelValue, ALPHA_CHANNEL_ONLY);
+      WriteChannelTable(pixelData, channel, channelValue, ALPHA_ONLY_OFFSET_TABLE);
       break;
     }
     case Dali::Pixel::L8:
     {
-      WriteChannel(pixelData, channel, channelValue, LUMINANCE_CHANNEL_ONLY);
+      WriteChannelTable(pixelData, channel, channelValue, LUMINANCE_ONLY_OFFSET_TABLE);
       break;
     }
     case Dali::Pixel::LA88:
     {
-      WriteChannel(pixelData, channel, channelValue, LUMINANCE_ALPHA_CHANNELS);
+      WriteChannelTable(pixelData, channel, channelValue, LUMINANCE_ALPHA_OFFSET_TABLE);
       break;
     }
     case Dali::Pixel::RGB565:
@@ -524,25 +517,25 @@ void WriteChannel(unsigned char*      pixelData,
     case Dali::Pixel::RGB888:
     case Dali::Pixel::RGB8888:
     {
-      WriteChannel(pixelData, channel, channelValue, RGB_CHANNELS);
+      WriteChannelTable(pixelData, channel, channelValue, RGB_OFFSET_TABLE);
       break;
     }
 
     case Dali::Pixel::BGR8888:
     {
-      WriteChannel(pixelData, channel, channelValue, BGR_CHANNELS);
+      WriteChannelTable(pixelData, channel, channelValue, BGR_OFFSET_TABLE);
       break;
     }
 
     case Dali::Pixel::RGBA8888:
     {
-      WriteChannel(pixelData, channel, channelValue, RGBA_CHANNELS);
+      WriteChannelTable(pixelData, channel, channelValue, RGBA_OFFSET_TABLE);
       break;
     }
 
     case Dali::Pixel::BGRA8888:
     {
-      WriteChannel(pixelData, channel, channelValue, BGRA_CHANNELS);
+      WriteChannelTable(pixelData, channel, channelValue, BGRA_OFFSET_TABLE);
       break;
     }