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>
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.
*/
}
// 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;
}
}
// 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;
}
}
// 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;
}
/*
- * 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.
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;
+ }
}
}
}
{
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);
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;
#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.
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
}
}
+// 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,
{
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,
{
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,
{
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,
{
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.");
+ }
}
}
}
DownscaleInPlacePow2Generic<1, HalveScanlineInPlace1Byte, AverageScanlines1>(pixels, inputWidth, inputHeight, desiredWidth, desiredHeight, dimensionTest, outWidth, outHeight);
}
+// Point sampling group below
+
namespace
{
/**
// 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
// 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
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;
}
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;
}
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;
}
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;
/*
- * 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.
{
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.
* @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;
}
* @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);
}
}
{
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:
{
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:
{
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:
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;
}