From 5bf461c739a17c68772a91d39cf264cdd70beec2 Mon Sep 17 00:00:00 2001 From: David Steele Date: Tue, 6 Jun 2017 10:18:36 +0100 Subject: [PATCH] Implemented basic CPU image masking Given a PixelData containing an image and a PixelData to use as a mask, this will apply the alpha channel of the mask to the image; either multiplying into the image's alpha channel, or if it doesn't have one, converting the image to RGBA888 and applying the mask's alpha channel. If the mask is a different size than the image, then this will sample a single pixel from a smaller mask, or use bilinear filtering to sample from a larger mask. Change-Id: I48b5d332627a4fb85899883868d278bcbc2b9c8e --- automated-tests/src/dali-internal/CMakeLists.txt | 1 + .../dali-internal/utc-Dali-Internal-PixelData.cpp | 186 ++++++ .../dali-test-suite-utils/test-compare-types.h | 9 +- automated-tests/src/dali/utc-Dali-PixelData.cpp | 131 ++++ dali/devel-api/file.list | 4 +- dali/devel-api/images/pixel-data-mask.cpp | 32 + dali/devel-api/images/pixel-data-mask.h | 36 ++ dali/internal/event/images/pixel-data-impl.cpp | 237 ++++++- dali/internal/event/images/pixel-data-impl.h | 40 +- dali/internal/event/images/pixel-manipulation.cpp | 693 +++++++++++++++++++++ dali/internal/event/images/pixel-manipulation.h | 83 +++ dali/internal/file.list | 1 + 12 files changed, 1437 insertions(+), 16 deletions(-) create mode 100644 automated-tests/src/dali-internal/utc-Dali-Internal-PixelData.cpp create mode 100644 dali/devel-api/images/pixel-data-mask.cpp create mode 100644 dali/devel-api/images/pixel-data-mask.h create mode 100644 dali/internal/event/images/pixel-manipulation.cpp create mode 100644 dali/internal/event/images/pixel-manipulation.h diff --git a/automated-tests/src/dali-internal/CMakeLists.txt b/automated-tests/src/dali-internal/CMakeLists.txt index 56ae01c..91eb1f4 100644 --- a/automated-tests/src/dali-internal/CMakeLists.txt +++ b/automated-tests/src/dali-internal/CMakeLists.txt @@ -12,6 +12,7 @@ SET(TC_SOURCES utc-Dali-Internal-MemoryPoolObjectAllocator.cpp utc-Dali-Internal-FrustumCulling.cpp utc-Dali-Internal-ActorDepth.cpp + utc-Dali-Internal-PixelData.cpp ) LIST(APPEND TC_SOURCES diff --git a/automated-tests/src/dali-internal/utc-Dali-Internal-PixelData.cpp b/automated-tests/src/dali-internal/utc-Dali-Internal-PixelData.cpp new file mode 100644 index 0000000..47535ec --- /dev/null +++ b/automated-tests/src/dali-internal/utc-Dali-Internal-PixelData.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2014 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +#include + +// Internal headers are allowed here + +#include + +using namespace Dali; +using namespace Dali::Internal::Pixel; +void utc_dali_internal_pixel_data_startup() +{ + test_return_value = TET_UNDEF; +} + +void utc_dali_internal_pixel_data_cleanup() +{ + test_return_value = TET_PASS; +} + +const char* ChannelToString( Dali::Internal::Pixel::Channel channel ) +{ + switch(channel) + { + case LUMINANCE: return "Luminance"; + case RED: return "Red"; + case GREEN: return "Green"; + case BLUE: return "Blue"; + case ALPHA: return "Alpha"; + default: + return "Unknown"; + } +} + +const char* FormatToString( Dali::Pixel::Format format ) +{ + switch(format) + { + case Dali::Pixel::A8: return "A8"; + case Dali::Pixel::L8: return "L8"; + case Dali::Pixel::LA88: return "LA88"; + case Dali::Pixel::RGB565: return "RGB565"; + case Dali::Pixel::BGR565: return "BGR565"; + case Dali::Pixel::RGBA4444: return "RGBA4444"; + case Dali::Pixel::BGRA4444: return "BGRA4444"; + case Dali::Pixel::RGBA5551: return "RGBA5551"; + case Dali::Pixel::BGRA5551: return "BGRA5551"; + + case Dali::Pixel::RGB888: return "RGB888"; + case Dali::Pixel::RGBA8888: return "RGBA8888"; + case Dali::Pixel::BGRA8888: return "BGRA8888"; + + default: + return "Unknown"; + } +} + + +int UtcDaliPixelManipulation01(void) +{ + tet_infoline("Testing Dali::Internal::PixelManipulation HasChannel"); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::A8, Dali::Internal::Pixel::ALPHA ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::A8, Dali::Internal::Pixel::LUMINANCE ), false, TEST_LOCATION ); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::L8, Dali::Internal::Pixel::LUMINANCE ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::L8, Dali::Internal::Pixel::ALPHA ), false, TEST_LOCATION ); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::LA88, Dali::Internal::Pixel::LUMINANCE ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::LA88, Dali::Internal::Pixel::ALPHA ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::LA88, Dali::Internal::Pixel::RED ), false, TEST_LOCATION ); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGB565, Dali::Internal::Pixel::RED ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGB565, Dali::Internal::Pixel::GREEN ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGB565, Dali::Internal::Pixel::BLUE ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGB565, Dali::Internal::Pixel::LUMINANCE ), false, TEST_LOCATION ); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGBA8888, Dali::Internal::Pixel::RED ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGBA8888, Dali::Internal::Pixel::GREEN ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGBA8888, Dali::Internal::Pixel::BLUE ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGBA8888, Dali::Internal::Pixel::ALPHA ), true, TEST_LOCATION ); + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::RGBA8888, Dali::Internal::Pixel::LUMINANCE ), false, TEST_LOCATION ); + + DALI_TEST_EQUALS( Dali::Internal::Pixel::HasChannel( Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR, Dali::Internal::Pixel::BLUE ), false, TEST_LOCATION ); + + END_TEST; +} + + + +int UtcDaliPixelManipulation02(void) +{ + tet_infoline("Testing Dali::Internal::PixelManipulation Read/WriteChannel"); + + unsigned char pixel[4] = {0,0,0,0}; + + for( int formatIdx=1; formatIdx(formatIdx); + Dali::Internal::Pixel::Channel channel = static_cast(channelIdx); + if( Dali::Internal::Pixel::HasChannel( format, channel ) ) + { + Dali::Internal::Pixel::WriteChannel( &pixel[0], format, channel, 0x15); + unsigned int value = Dali::Internal::Pixel::ReadChannel( &pixel[0], format, channel ); + + tet_printf( "Testing writing and reading to %s channel in %s format:\n", + ChannelToString(channel), FormatToString(format) ); + + if( channel == Dali::Internal::Pixel::ALPHA && (format == Dali::Pixel::RGBA5551 || format == Dali::Pixel::BGRA5551 ) ) + { + DALI_TEST_EQUALS( value, 0x1, TEST_LOCATION ); + } + else if( format == Dali::Pixel::RGBA4444 || format == Dali::Pixel::BGRA4444 ) + { + DALI_TEST_EQUALS( value, 0x05, TEST_LOCATION ); + } + else + { + DALI_TEST_EQUALS( value, 0x15, TEST_LOCATION ); + } + } + } + } + + END_TEST; +} + + +int UtcDaliPixelManipulation03N(void) +{ + tet_infoline("Testing Dali::Internal::PixelManipulation Read/WriteChannel"); + + unsigned char pixel[4] = {0,0,0,0}; + + for( int formatIdx=1; formatIdx(formatIdx); + Dali::Internal::Pixel::Channel channel = static_cast(channelIdx); + if( ! Dali::Internal::Pixel::HasChannel( format, channel ) ) + { + unsigned int value = Dali::Internal::Pixel::ReadChannel( &pixel[0], format, channel ); + + tet_printf( "Testing reading from %s channel in %s format:\n", + ChannelToString(channel), FormatToString(format) ); + + DALI_TEST_EQUALS( value, 0x00, TEST_LOCATION ); + } + } + } + + END_TEST; +} diff --git a/automated-tests/src/dali/dali-test-suite-utils/test-compare-types.h b/automated-tests/src/dali/dali-test-suite-utils/test-compare-types.h index 248276e..dd6ba24 100644 --- a/automated-tests/src/dali/dali-test-suite-utils/test-compare-types.h +++ b/automated-tests/src/dali/dali-test-suite-utils/test-compare-types.h @@ -173,9 +173,16 @@ inline bool CompareType(Property::Value q1, Property::Value q2, result = CompareType(a, b, epsilon); break; } + case Property::STRING: + { + std::string a, b; + q1.Get(a); + q2.Get(b); + result = (a.compare(b) == 0); + break; + } case Property::MATRIX: case Property::MATRIX3: - case Property::STRING: case Property::ARRAY: case Property::MAP: { diff --git a/automated-tests/src/dali/utc-Dali-PixelData.cpp b/automated-tests/src/dali/utc-Dali-PixelData.cpp index 6151545..b9b3d87 100644 --- a/automated-tests/src/dali/utc-Dali-PixelData.cpp +++ b/automated-tests/src/dali/utc-Dali-PixelData.cpp @@ -20,6 +20,7 @@ #include #include +#include #include using namespace Dali; @@ -98,3 +99,133 @@ int UtcDaliPixelDataAssignmentOperator(void) END_TEST; } + +int UtcDaliPixelDataMask01(void) +{ + TestApplication application; + + unsigned int width = 10u; + unsigned int height = 10u; + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::L8 ); + unsigned char* buffer = new unsigned char [ bufferSize ]; + PixelData maskData = PixelData::New( buffer, bufferSize, width, height, Pixel::L8, PixelData::DELETE_ARRAY ); + + width = 20u; + height = 20u; + Pixel::Format pixelFormat = Pixel::RGBA5551; + bufferSize = width*height*Pixel::GetBytesPerPixel( pixelFormat ); + buffer = new unsigned char [ bufferSize ]; + PixelData imageData = PixelData::New( buffer, bufferSize, width, height, pixelFormat, PixelData::DELETE_ARRAY ); + + Dali::ApplyMask( imageData, maskData ); + + // Test that the pixel format has been promoted to RGBA8888 + DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliPixelDataMask02(void) +{ + TestApplication application; + + unsigned int width = 10u; + unsigned int height = 10u; + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::L8 ); + unsigned char* buffer = new unsigned char [ bufferSize ]; + PixelData maskData = PixelData::New( buffer, bufferSize, width, height, Pixel::L8, PixelData::DELETE_ARRAY ); + + width = 20u; + height = 20u; + Pixel::Format pixelFormat = Pixel::RGBA4444; + bufferSize = width*height*Pixel::GetBytesPerPixel( pixelFormat ); + buffer = new unsigned char [ bufferSize ]; + PixelData imageData = PixelData::New( buffer, bufferSize, width, height, pixelFormat, PixelData::DELETE_ARRAY ); + + Dali::ApplyMask( imageData, maskData ); + + // Test that the pixel format has been promoted to RGBA8888 + DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION ); + + END_TEST; +} + +int UtcDaliPixelDataMask03(void) +{ + TestApplication application; + + unsigned int width = 20u; + unsigned int height = 20u; + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::L8 ); + unsigned char* buffer = new unsigned char [ bufferSize ]; + PixelData maskData = PixelData::New( buffer, bufferSize, width, height, Pixel::L8, PixelData::DELETE_ARRAY ); + + width = 10u; + height = 10u; + Pixel::Format format = Pixel::RGB565; + bufferSize = width*height*Pixel::GetBytesPerPixel( format ); + buffer = new unsigned char [ bufferSize ]; + PixelData imageData = PixelData::New( buffer, bufferSize, width, height, format, PixelData::DELETE_ARRAY ); + + Dali::ApplyMask( imageData, maskData ); + + // Test that the pixel format has been promoted to RGBA8888 + DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION ); + + // Can't test the final image directly... + + END_TEST; +} + + +int UtcDaliPixelDataMask04(void) +{ + TestApplication application; + + unsigned int width = 10u; + unsigned int height = 10u; + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::L8 ); + unsigned char* buffer = new unsigned char [ bufferSize ]; + PixelData maskData = PixelData::New( buffer, bufferSize, width, height, Pixel::L8, PixelData::DELETE_ARRAY ); + + width = 20u; + height = 20u; + bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 ); + buffer = new unsigned char [ bufferSize ]; + PixelData imageData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::DELETE_ARRAY ); + + Dali::ApplyMask( imageData, maskData ); + + // Test that the pixel format hasn't changed + DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION ); + + // Can't test the final image directly... + + END_TEST; +} + +int UtcDaliPixelDataMask05(void) +{ + TestApplication application; + + unsigned int width = 20u; + unsigned int height = 20u; + unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::L8 ); + unsigned char* buffer = new unsigned char [ bufferSize ]; + PixelData maskData = PixelData::New( buffer, bufferSize, width, height, Pixel::L8, PixelData::DELETE_ARRAY ); + + width = 10u; + height = 10u; + bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 ); + buffer = new unsigned char [ bufferSize ]; + PixelData imageData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::DELETE_ARRAY ); + + Dali::ApplyMask( imageData, maskData ); + + // Test that the pixel format hasn't changed + DALI_TEST_EQUALS( imageData.GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION ); + + // Can't test the final image directly... + + END_TEST; +} diff --git a/dali/devel-api/file.list b/dali/devel-api/file.list index 2823573..b44c3f9 100644 --- a/dali/devel-api/file.list +++ b/dali/devel-api/file.list @@ -9,6 +9,7 @@ devel_api_src_files = \ $(devel_api_src_dir)/images/distance-field.cpp \ $(devel_api_src_dir)/images/texture-set-image.cpp \ $(devel_api_src_dir)/images/nine-patch-image.cpp \ + $(devel_api_src_dir)/images/pixel-data-mask.cpp \ $(devel_api_src_dir)/object/handle-devel.cpp \ $(devel_api_src_dir)/object/weak-handle.cpp \ $(devel_api_src_dir)/object/csharp-type-registry.cpp \ @@ -44,7 +45,8 @@ devel_api_core_images_header_files = \ $(devel_api_src_dir)/images/distance-field.h \ $(devel_api_src_dir)/images/native-image-interface-extension.h \ $(devel_api_src_dir)/images/texture-set-image.h \ - $(devel_api_src_dir)/images/nine-patch-image.h + $(devel_api_src_dir)/images/nine-patch-image.h \ + $(devel_api_src_dir)/images/pixel-data-mask.h devel_api_core_object_header_files = \ $(devel_api_src_dir)/object/csharp-type-info.h \ diff --git a/dali/devel-api/images/pixel-data-mask.cpp b/dali/devel-api/images/pixel-data-mask.cpp new file mode 100644 index 0000000..448a286 --- /dev/null +++ b/dali/devel-api/images/pixel-data-mask.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +namespace Dali +{ + +void ApplyMask( PixelData image, PixelData mask ) +{ + if( image && mask ) + { + GetImplementation(image).ApplyMask( GetImplementation(mask) ); + } +} + + +} // namespace Dali diff --git a/dali/devel-api/images/pixel-data-mask.h b/dali/devel-api/images/pixel-data-mask.h new file mode 100644 index 0000000..d0e8e56 --- /dev/null +++ b/dali/devel-api/images/pixel-data-mask.h @@ -0,0 +1,36 @@ +#ifndef DALI_DEVEL_API_IMAGES_PIXEL_DATA_MASK_H +#define DALI_DEVEL_API_IMAGES_PIXEL_DATA_MASK_H + +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace Dali +{ + +/** + * @brief Apply the mask to this pixel data. + * If the mask image size is larger, then this will sample from + * the mask using bilinear filtering. + * @param[in] image The pixel data of the image + * @param[in] mask The pixel data of the mask + */ +DALI_IMPORT_API void ApplyMask( PixelData image, PixelData mask ); + +} // namespace Dali + +#endif // DALI_DEVEL_API_IMAGES_PIXEL_DATA_MASK_H diff --git a/dali/internal/event/images/pixel-data-impl.cpp b/dali/internal/event/images/pixel-data-impl.cpp index 44a7e1d..41c0aed 100644 --- a/dali/internal/event/images/pixel-data-impl.cpp +++ b/dali/internal/event/images/pixel-data-impl.cpp @@ -21,6 +21,9 @@ // EXTERNAL INCLUDES #include +// INTERNAL INCLUDES +#include + namespace Dali { @@ -31,7 +34,7 @@ PixelData::PixelData( unsigned char* buffer, unsigned int bufferSize, unsigned int width, unsigned int height, - Pixel::Format pixelFormat, + Dali::Pixel::Format pixelFormat, Dali::PixelData::ReleaseFunction releaseFunction ) : mBuffer( buffer ), mBufferSize( bufferSize ), @@ -46,22 +49,15 @@ PixelData::~PixelData() { if( mBuffer ) { - if( mReleaseFunction == Dali::PixelData::FREE) - { - free( mBuffer ); - } - else - { - delete[] mBuffer; - } + ReleaseBuffer(); } - } +} PixelDataPtr PixelData::New(unsigned char* buffer, unsigned int bufferSize, unsigned int width, unsigned int height, - Pixel::Format pixelFormat, + Dali::Pixel::Format pixelFormat, Dali::PixelData::ReleaseFunction releaseFunction) { return new PixelData( buffer, bufferSize, width, height, pixelFormat, releaseFunction ); @@ -77,7 +73,7 @@ unsigned int PixelData::GetHeight() const return mHeight; } -Pixel::Format PixelData::GetPixelFormat() const +Dali::Pixel::Format PixelData::GetPixelFormat() const { return mPixelFormat; } @@ -92,6 +88,223 @@ unsigned int PixelData::GetBufferSize() const return mBufferSize; } +void PixelData::ReleaseBuffer() +{ + if( mReleaseFunction == Dali::PixelData::FREE) + { + free( mBuffer ); + } + else + { + delete[] mBuffer; + } +} + +void PixelData::ApplyMask( const PixelData& mask ) +{ + int byteOffset=0; + int bitMask=0; + Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask); + + if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 ) + { + ApplyMaskToAlphaChannel( mask ); + } + else + { + AddAlphaChannel(mask); + } +} + +void PixelData::ApplyMaskToAlphaChannel( const PixelData& mask ) +{ + const float rowFactor = float(mask.mHeight) / (1.0f * mHeight); + const float colFactor = float(mask.mWidth) / (1.0f * mWidth) ; + + int numSamples = 1; + if( mask.mHeight > mHeight || mask.mWidth > mWidth ) + { + numSamples = 4; + } + + int srcAlphaByteOffset=0; + int srcAlphaMask=0; + Dali::Pixel::GetAlphaOffsetAndMask( mask.mPixelFormat, srcAlphaByteOffset, srcAlphaMask ); + + int destAlphaByteOffset=0; + int destAlphaMask=0; + Dali::Pixel::GetAlphaOffsetAndMask( mPixelFormat, destAlphaByteOffset, destAlphaMask ); + + unsigned int srcBytesPerPixel = Dali::Pixel::GetBytesPerPixel( mask.mPixelFormat ); + unsigned int destBytesPerPixel = Dali::Pixel::GetBytesPerPixel( mPixelFormat ); + int srcStride = mask.mWidth * srcBytesPerPixel; + + int srcOffset=0; + int destOffset=0; + + float srcAlphaValue = 1.0f; + + for( unsigned int row = 0; row < mHeight; ++row ) + { + for( unsigned int col = 0; col < mWidth; ++col ) + { + if( numSamples == 1 ) + { + srcOffset = floorf(row * rowFactor) * srcStride + floorf(col * colFactor) * srcBytesPerPixel; + unsigned char alpha = mask.mBuffer[srcOffset + srcAlphaByteOffset] & srcAlphaMask; + srcAlphaValue = float(alpha)/255.0f; + } + else + { + srcAlphaValue = mask.ReadWeightedSample( col*colFactor, row*rowFactor ); + } + + unsigned char destAlpha = mBuffer[destOffset + destAlphaByteOffset] & destAlphaMask; + float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f); + destAlpha = destAlphaValue; + mBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask; + mBuffer[destOffset + destAlphaByteOffset] |= ( destAlpha & destAlphaMask ); + + destOffset += destBytesPerPixel; + } + } +} + +void PixelData::AddAlphaChannel( const PixelData& mask ) +{ + const float rowFactor = float(mask.mHeight) / (1.0f * mHeight); + const float colFactor = float(mask.mWidth) / (1.0f * mWidth) ; + + int numSamples = 1; + if( mask.mHeight > mHeight || mask.mWidth > mWidth ) + { + numSamples = 4; + } + + // Set up source alpha offsets + int srcAlphaByteOffset=0; + int srcAlphaMask=0; + Dali::Pixel::GetAlphaOffsetAndMask( mask.mPixelFormat, srcAlphaByteOffset, srcAlphaMask ); + + unsigned int srcAlphaBytesPerPixel = Dali::Pixel::GetBytesPerPixel( mask.mPixelFormat ); + int srcAlphaStride = mask.mWidth * srcAlphaBytesPerPixel; + + // Set up source color offsets + unsigned int srcColorBytesPerPixel = Dali::Pixel::GetBytesPerPixel( mPixelFormat ); + + // Setup destination offsets + Dali::Pixel::Format destPixelFormat = Dali::Pixel::RGBA8888; + unsigned int destBytesPerPixel = Dali::Pixel::GetBytesPerPixel( destPixelFormat ); + int destAlphaByteOffset=0; + int destAlphaMask=0; + Dali::Pixel::GetAlphaOffsetAndMask( destPixelFormat, destAlphaByteOffset, destAlphaMask ); + + int destBufferSize = mWidth * mHeight * destBytesPerPixel; + unsigned char* destBuffer = static_cast( malloc( destBufferSize ) ); + + int srcAlphaOffset=0; + int srcColorOffset=0; + int destOffset=0; + bool hasAlpha = Dali::Pixel::HasAlpha(mPixelFormat); + + float srcAlphaValue = 1.0f; + unsigned char destAlpha = 0; + + for( unsigned int row = 0; row < mHeight; ++row ) + { + for( unsigned int col = 0; col < mWidth; ++col ) + { + if( numSamples == 1 ) + { + srcAlphaOffset = floorf(row * rowFactor) * srcAlphaStride + floorf(col * colFactor) * srcAlphaBytesPerPixel; + unsigned char alpha = mask.mBuffer[srcAlphaOffset + srcAlphaByteOffset] & srcAlphaMask; + srcAlphaValue = float(alpha)/255.0f; + } + else + { + srcAlphaValue = mask.ReadWeightedSample( col*colFactor, row*rowFactor ); + } + + Pixel::ConvertColorChannelsToRGBA8888(mBuffer, srcColorOffset, mPixelFormat, destBuffer, destOffset ); + + if( hasAlpha ) + { + destAlpha = mBuffer[destOffset + destAlphaByteOffset] & destAlphaMask; + float destAlphaValue = Clamp(float(destAlpha) * srcAlphaValue, 0.0f, 255.0f); + destAlpha = destAlphaValue; + } + else + { + destAlpha = floorf(Clamp(srcAlphaValue * 255.0f, 0.0f, 255.0f)); + } + + destBuffer[destOffset + destAlphaByteOffset] &= ~destAlphaMask; + destBuffer[destOffset + destAlphaByteOffset] |= ( destAlpha & destAlphaMask ); + + srcColorOffset += srcColorBytesPerPixel; + destOffset += destBytesPerPixel; + } + } + + ReleaseBuffer(); + + mBuffer = destBuffer; + mBufferSize = destBufferSize; + mPixelFormat = Dali::Pixel::RGBA8888; + mReleaseFunction = Dali::PixelData::FREE; +} + +float PixelData::ReadWeightedSample( float x, float y ) const +{ + unsigned int srcRow = floorf( y ); + unsigned int srcCol = floorf( x ); + + int bytesPerPixel = Dali::Pixel::GetBytesPerPixel( mPixelFormat ); + int stride = mWidth * bytesPerPixel; + int srcOffset = srcRow * stride + srcCol * bytesPerPixel; + float samples[4]; + + samples[0] = ReadChannel( mBuffer + srcOffset, mPixelFormat, Pixel::ALPHA ); + + if( srcCol < mWidth-1 ) + { + samples[1] = ReadChannel( mBuffer + srcOffset+bytesPerPixel, mPixelFormat, Pixel::ALPHA ); + } + else + { + samples[1] = samples[0]; + } + + if( srcRow < mHeight-1 ) + { + samples[2] = ReadChannel( mBuffer + stride + srcOffset, mPixelFormat, Pixel::ALPHA ); + } + else + { + samples[2] = samples[0]; + } + + if( srcRow < mHeight-1 && srcCol < mWidth-1 ) + { + samples[3] = ReadChannel( mBuffer + stride + srcOffset + bytesPerPixel, mPixelFormat, Pixel::ALPHA ); + } + else + { + samples[3] = samples[2]; + } + + // Bilinear interpolation: + float weight[4]; + weight[0] = float(srcRow+1.0f) - y; + weight[1] = y - float(srcRow); + weight[2] = float(srcCol+1.0f) - x; + weight[3] = x - float(srcCol); + + return ( weight[2] * (samples[0] * weight[0] + samples[1] * weight[1]) + + weight[3] * (samples[2] * weight[0] + samples[3] * weight[1]) ) / 256.0f; +} + + }// namespace Internal }// namespace Dali diff --git a/dali/internal/event/images/pixel-data-impl.h b/dali/internal/event/images/pixel-data-impl.h index 4d45f73..6c49ede 100644 --- a/dali/internal/event/images/pixel-data-impl.h +++ b/dali/internal/event/images/pixel-data-impl.h @@ -110,9 +110,45 @@ public: */ unsigned int GetBufferSize() const; + /** + * Apply the mask to this data + * @param[in] mask The mask to apply + */ + void ApplyMask( const PixelData& mask ); + private: + /** + * Release the buffer + */ + void ReleaseBuffer(); - /* + /** + * Apply the mask to this data's alpha channel + * @param[in] mask The mask to apply + */ + void ApplyMaskToAlphaChannel( const PixelData& mask ); + + /** + * Convert to RGBA8888 and apply the mask's alpha channel + * to this data's alpha channel + * @param[in] mask The mask to apply + */ + void AddAlphaChannel( const PixelData& mask ); + + /** + * Apply the mask to this data's color channels (e.g. to apply vignette) + * @param[in] mask The mask to apply + */ + void ApplyMaskToColorChannels( const PixelData& mask ); + + /** + * Read a weighted sample from a pixel (of unknown size) + * @param[in] x The x coordinate to sample from + * @param[in] y The y coordinate to sample from + */ + float ReadWeightedSample( float x, float y ) const; + + /* * Undefined copy constructor. */ PixelData(const PixelData& other); @@ -120,7 +156,7 @@ private: /* * Undefined assignment operator. */ - PixelData& operator = (const PixelData& other); + PixelData& operator= (const PixelData& other); private: diff --git a/dali/internal/event/images/pixel-manipulation.cpp b/dali/internal/event/images/pixel-manipulation.cpp new file mode 100644 index 0000000..b8f5693 --- /dev/null +++ b/dali/internal/event/images/pixel-manipulation.cpp @@ -0,0 +1,693 @@ +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// CLASS HEADER +#include + +// INTERNAL HEADERS +#include +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Pixel +{ + +struct Location +{ + unsigned int bitShift; + unsigned int bitMask; + bool available; +}; + +struct Locations +{ + Location luminance; + Location alpha; + Location red; + Location green; + Location blue; +}; + + +bool HasChannel( Dali::Pixel::Format pixelFormat, Channel channel ) +{ + switch (pixelFormat) + { + case Dali::Pixel::A8: + { + return (channel == ALPHA); + } + case Dali::Pixel::L8: + { + return (channel == LUMINANCE); + } + case Dali::Pixel::LA88: + { + return ( channel == LUMINANCE || channel == ALPHA ); + } + case Dali::Pixel::RGB565: + case Dali::Pixel::BGR565: + case Dali::Pixel::RGB888: + case Dali::Pixel::RGB8888: + case Dali::Pixel::BGR8888: + { + return ( channel == RED || channel == GREEN || channel == BLUE ); + } + + case Dali::Pixel::RGBA8888: + case Dali::Pixel::BGRA8888: + case Dali::Pixel::RGBA4444: + case Dali::Pixel::BGRA4444: + case Dali::Pixel::RGBA5551: + case Dali::Pixel::BGRA5551: + { + return ( channel == RED || channel == GREEN || channel == BLUE || channel == ALPHA ); + } + + case Dali::Pixel::INVALID: + case Dali::Pixel::COMPRESSED_R11_EAC: + case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC: + case Dali::Pixel::COMPRESSED_RG11_EAC: + case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC: + case Dali::Pixel::COMPRESSED_RGB8_ETC2: + case Dali::Pixel::COMPRESSED_SRGB8_ETC2: + case Dali::Pixel::COMPRESSED_RGB8_ETC1: + case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1: + case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2: + case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR: + case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR: + case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR: + { + DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple channels.\n"); + break; + } + } + + return false; +} + +unsigned int ReadChannel( unsigned char* pixelData, + Dali::Pixel::Format pixelFormat, + Channel channel ) +{ + switch (pixelFormat) + { + case Dali::Pixel::A8: + { + if( channel == ALPHA ) + { + return static_cast(*pixelData); + } + else return 0u; + } + case Dali::Pixel::L8: + { + if( channel == LUMINANCE ) + { + return static_cast(*pixelData); + } + else return 0u; + } + case Dali::Pixel::LA88: + { + if( channel == LUMINANCE ) + { + return static_cast(*pixelData); + } + else if( channel == ALPHA ) + { + return static_cast(*(pixelData+1)); + } + else return 0u; + } + case Dali::Pixel::RGB565: + { + if( channel == RED ) + { + return (static_cast(*pixelData) & 0xF8) >> 3; + } + else if( channel == GREEN ) + { + return ((static_cast(*pixelData) & 0x07) << 3) | + ((static_cast(*(pixelData+1)) & 0xE0) >> 5); + } + else if( channel == BLUE ) + { + return static_cast(*(pixelData+1)) & 0x1F; + } + else return 0u; + } + + case Dali::Pixel::BGR565: + { + if( channel == BLUE ) + { + return (static_cast(*pixelData) & 0xF8) >> 3; + } + else if( channel == GREEN ) + { + return ((static_cast(*pixelData) & 0x07) << 3) | + ((static_cast(*(pixelData+1)) & 0xE0) >> 5); + } + else if( channel == RED ) + { + return (static_cast(*(pixelData+1) & 0x1F) ); + } + else return 0u; + } + + case Dali::Pixel::RGB888: + case Dali::Pixel::RGB8888: + { + if( channel == RED ) + { + return static_cast(*pixelData); + } + else if( channel == GREEN ) + { + return static_cast(*(pixelData+1)); + } + else if( channel == BLUE ) + { + return static_cast(*(pixelData+2)); + } + else return 0u; + } + + case Dali::Pixel::BGR8888: + { + if( channel == BLUE ) + { + return static_cast(*pixelData); + } + else if( channel == GREEN ) + { + return static_cast(*(pixelData+1)); + } + else if( channel == RED ) + { + return static_cast(*(pixelData+2)); + } + else return 0u; + } + + case Dali::Pixel::RGBA8888: + { + if( channel == RED ) + { + return static_cast(*pixelData); + } + else if( channel == GREEN ) + { + return static_cast(*(pixelData+1)); + } + else if( channel == BLUE ) + { + return static_cast(*(pixelData+2)); + } + else if( channel == ALPHA ) + { + return static_cast(*(pixelData+3)); + } + else return 0u; + } + + case Dali::Pixel::BGRA8888: + { + if( channel == BLUE ) + { + return static_cast(*pixelData); + } + else if( channel == GREEN ) + { + return static_cast(*(pixelData+1)); + } + else if( channel == RED ) + { + return static_cast(*(pixelData+2)); + } + else if( channel == ALPHA ) + { + return static_cast(*(pixelData+3)); + } + else return 0u; + } + + case Dali::Pixel::RGBA4444: + { + if( channel == RED ) + { + return (static_cast(*pixelData) & 0xF0) >> 4; + } + else if( channel == GREEN ) + { + return (static_cast(*pixelData) & 0x0F); + } + else if( channel == BLUE ) + { + return (static_cast(*(pixelData+1)) & 0xF0) >> 4; + } + else if( channel == ALPHA ) + { + return (static_cast(*(pixelData+1)) & 0x0F); + } + else return 0u; + } + + case Dali::Pixel::BGRA4444: + { + if( channel == BLUE ) + { + return (static_cast(*pixelData) & 0xF0) >> 4; + } + else if( channel == GREEN ) + { + return (static_cast(*pixelData) & 0x0F); + } + else if( channel == RED ) + { + return (static_cast(*(pixelData+1)) & 0xF0) >> 4; + } + else if( channel == ALPHA ) + { + return (static_cast(*(pixelData+1)) & 0x0F); + } + else return 0u; + } + + case Dali::Pixel::RGBA5551: + { + if( channel == RED ) + { + return (static_cast(*pixelData) & 0xF8) >> 3; + } + else if( channel == GREEN ) + { + return ((static_cast(*pixelData) & 0x07) << 2) | + ((static_cast(*(pixelData+1)) & 0xC0) >> 6); + } + else if( channel == BLUE ) + { + return (static_cast(*(pixelData+1)) & 0x3E) >> 1; + } + else if( channel == ALPHA ) + { + return static_cast(*(pixelData+1)) & 0x01; + } + + else return 0u; + } + + case Dali::Pixel::BGRA5551: + { + if( channel == BLUE ) + { + return (static_cast(*pixelData) & 0xF8) >> 3; + } + else if( channel == GREEN ) + { + return ((static_cast(*pixelData) & 0x07) << 2) | + ((static_cast(*(pixelData+1)) & 0xC0) >> 6); + } + else if( channel == RED ) + { + return ( static_cast(*(pixelData+1)) & 0x3E) >> 1; + } + else if( channel == ALPHA ) + { + return static_cast(*(pixelData+1)) & 0x01; + } + + else return 0u; + } + + default: + { + return 0u; + } + } +} + +void WriteChannel( unsigned char* pixelData, + Dali::Pixel::Format pixelFormat, + Channel channel, + unsigned int channelValue ) +{ + switch (pixelFormat) + { + case Dali::Pixel::A8: + { + if( channel == ALPHA ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + break; + } + case Dali::Pixel::L8: + { + if( channel == LUMINANCE ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + break; + } + case Dali::Pixel::LA88: + { + if( channel == LUMINANCE ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + else if( channel == ALPHA ) + { + *(pixelData+1) = static_cast( channelValue & 0xFF ); + } + break; + } + case Dali::Pixel::RGB565: + { + if( channel == RED ) + { + *pixelData &= ~0xF8; + *pixelData |= static_cast( (channelValue << 3) & 0xF8 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x07; + *pixelData |= static_cast( (channelValue >> 3) & 0x07 ); + + *(pixelData+1) &= ~0xE0; + *(pixelData+1) |= static_cast( (channelValue << 5) & 0xE0 ); + } + else if( channel == BLUE ) + { + *(pixelData+1) &= ~0x1F; + *(pixelData+1) |= static_cast( channelValue & 0x1F ); + } + break; + } + + case Dali::Pixel::BGR565: + { + if( channel == BLUE ) + { + *pixelData &= ~0xF8; + *pixelData |= static_cast( (channelValue << 3) & 0xF8 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x07; + *pixelData |= static_cast( (channelValue >> 3) & 0x07 ); + + *(pixelData+1) &= ~0xE0; + *(pixelData+1) |= static_cast( (channelValue << 5) & 0xE0 ); + } + else if( channel == RED ) + { + *(pixelData+1) &= ~0x1F; + *(pixelData+1) |= static_cast( channelValue & 0x1F ); + } + break; + } + + case Dali::Pixel::RGB888: + case Dali::Pixel::RGB8888: + { + if( channel == RED ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + else if( channel == GREEN ) + { + *(pixelData+1) = static_cast( channelValue & 0xFF ); + } + else if( channel == BLUE ) + { + *(pixelData+2) = static_cast( channelValue & 0xFF ); + } + break; + } + + case Dali::Pixel::BGR8888: + { + if( channel == BLUE ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + else if( channel == GREEN ) + { + *(pixelData+1) = static_cast( channelValue & 0xFF ); + } + else if( channel == RED ) + { + *(pixelData+2) = static_cast( channelValue & 0xFF ); + } + break; + } + + case Dali::Pixel::RGBA8888: + { + if( channel == RED ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + else if( channel == GREEN ) + { + *(pixelData+1) = static_cast( channelValue & 0xFF ); + } + else if( channel == BLUE ) + { + *(pixelData+2) = static_cast( channelValue & 0xFF ); + } + else if( channel == ALPHA ) + { + *(pixelData+3) = static_cast( channelValue & 0xFF ); + } + break; + } + + case Dali::Pixel::BGRA8888: + { + if( channel == BLUE ) + { + *pixelData = static_cast( channelValue & 0xFF ); + } + else if( channel == GREEN ) + { + *(pixelData+1) = static_cast( channelValue & 0xFF ); + } + else if( channel == RED ) + { + *(pixelData+2) = static_cast( channelValue & 0xFF ); + } + else if( channel == ALPHA ) + { + *(pixelData+3) = static_cast( channelValue & 0xFF ); + } + break; + } + + case Dali::Pixel::RGBA4444: + { + if( channel == RED ) + { + *pixelData &= ~0xF0; + *pixelData |= static_cast( (channelValue << 4) & 0xF0 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x0F; + *pixelData |= static_cast( channelValue & 0x0F ); + } + else if( channel == BLUE ) + { + *(pixelData+1) &= ~0xF0; + *(pixelData+1) |= static_cast( (channelValue << 4) & 0xF0 ); + } + else if( channel == ALPHA ) + { + *(pixelData+1) &= ~0x0F; + *(pixelData+1) |= static_cast( channelValue & 0x0F ); + } + break; + } + + case Dali::Pixel::BGRA4444: + { + if( channel == BLUE ) + { + *pixelData &= ~0xF0; + *pixelData |= static_cast( (channelValue << 4) & 0xF0 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x0F; + *pixelData |= static_cast( channelValue & 0x0F ); + } + else if( channel == RED ) + { + *(pixelData+1) &= ~0xF0; + *(pixelData+1) |= static_cast( (channelValue << 4) & 0xF0 ); + } + else if( channel == ALPHA ) + { + *(pixelData+1) &= ~0x0F; + *(pixelData+1) |= static_cast( channelValue & 0x0F ); + } + break; + } + + case Dali::Pixel::RGBA5551: + { + // rrrrrggg ggbbbbba + // F8 7 C0 3E 1 + if( channel == RED ) + { + *pixelData &= ~0xF8; + *pixelData |= static_cast( (channelValue << 3) & 0xF8 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x07; + *pixelData |= static_cast( (channelValue >> 2) & 0x07 ); + + *(pixelData+1) &= ~0xC0; + *(pixelData+1) |= static_cast( (channelValue << 6) & 0xC0 ); + } + else if( channel == BLUE ) + { + *(pixelData+1) &= ~0x3E; + *(pixelData+1) |= static_cast( (channelValue << 1) & 0x3E ); + } + else if( channel == ALPHA ) + { + *(pixelData+1) &= ~0x01; + *(pixelData+1) |= static_cast( channelValue & 0x01 ); + } + break; + } + + case Dali::Pixel::BGRA5551: + { + if( channel == BLUE ) + { + *pixelData &= ~0xF8; + *pixelData |= static_cast( (channelValue << 3) & 0xF8 ); + } + else if( channel == GREEN ) + { + *pixelData &= ~0x07; + *pixelData |= static_cast( (channelValue >> 2) & 0x07 ); + + *(pixelData+1) &= ~0xC0; + *(pixelData+1) |= static_cast( (channelValue << 6) & 0xC0 ); + } + else if( channel == RED ) + { + *(pixelData+1) &= ~0x3E; + *(pixelData+1) |= static_cast( (channelValue << 1 ) & 0x3E ); + } + else if( channel == ALPHA ) + { + *(pixelData+1) &= ~0x01; + *(pixelData+1) |= static_cast( channelValue & 0x01 ); + } + break; + } + + default: + break; + } +} + +void ConvertColorChannelsToRGBA8888( + unsigned char* srcPixel, int srcOffset, Dali::Pixel::Format srcFormat, + unsigned char* destPixel, int destOffset ) +{ + int red = ReadChannel(srcPixel+srcOffset, srcFormat, RED ); + int green = ReadChannel(srcPixel+srcOffset, srcFormat, GREEN ); + int blue = ReadChannel(srcPixel+srcOffset, srcFormat, BLUE ); + switch( srcFormat ) + { + case Dali::Pixel::RGB565: + case Dali::Pixel::BGR565: + { + red<<=3; + green<<=2; + blue<<=3; + break; + } + case Dali::Pixel::RGBA4444: + case Dali::Pixel::BGRA4444: + { + red<<=4; + green<<=4; + blue<<=4; + break; + } + case Dali::Pixel::RGBA5551: + case Dali::Pixel::BGRA5551: + { + red<<=3; + green<<=3; + blue<<=3; + break; + } + default: + break; + } + WriteChannel(destPixel+destOffset, Dali::Pixel::RGBA8888, RED, red); + WriteChannel(destPixel+destOffset, Dali::Pixel::RGBA8888, GREEN, green); + WriteChannel(destPixel+destOffset, Dali::Pixel::RGBA8888, BLUE, blue); +} + + + +} // Pixel +} // Internal +} // Dali diff --git a/dali/internal/event/images/pixel-manipulation.h b/dali/internal/event/images/pixel-manipulation.h new file mode 100644 index 0000000..f9e6098 --- /dev/null +++ b/dali/internal/event/images/pixel-manipulation.h @@ -0,0 +1,83 @@ +#ifndef DALI_EVENT_IMAGES_PIXEL_MANIPULATION_H +#define DALI_EVENT_IMAGES_PIXEL_MANIPULATION_H + +/* + * Copyright (c) 2017 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + +namespace Dali +{ + +namespace Internal +{ + +namespace Pixel +{ + +enum Channel +{ + LUMINANCE, + RED, + GREEN, + BLUE, + ALPHA, + MAX_NUMBER_OF_CHANNELS +}; + +/** + * Return true if the channel exists in the pixel format + * @param[in] pixelFormat The pixelFormat + * @param[in] channel The channel to test for + * @return true if the channel exists + */ +bool HasChannel( Dali::Pixel::Format pixelFormat, Channel channel ); + + +/** + * Read a colour channel from the pixel with the given pixel format. + * Returns zero if the format does not support the channel + * @param[in] pixelData Location of the pixel + * @param[in] pixelFormat The format of the pixel + * @param[in] channel The channel to read + * @return the channel value + */ +unsigned int ReadChannel( unsigned char* pixelData, + Dali::Pixel::Format pixelFormat, + Channel channel ); + +/** + * Write a colour channel to the pixel with the given pixel format. + * @param[in] pixelData Location of the pixel + * @param[in] pixelFormat The format of the pixel + * @param[in] channel The channel to write + * @param[in] channelValue the value to write to the channel + */ +void WriteChannel( unsigned char* pixelData, + Dali::Pixel::Format pixelFormat, + Channel channel, + unsigned int channelValue ); + +void ConvertColorChannelsToRGBA8888( + unsigned char* srcPixel, int srcOffset, Dali::Pixel::Format srcFormat, + unsigned char* destPixel, int destOffset ); + +} // Pixel +} // Internal +} // Dali + + +#endif // DALI_EVENT_IMAGES_PIXEL_MANIPULATION_H diff --git a/dali/internal/file.list b/dali/internal/file.list index 4b98598..cf4ac67 100644 --- a/dali/internal/file.list +++ b/dali/internal/file.list @@ -74,6 +74,7 @@ internal_src_files = \ $(internal_src_dir)/event/images/resource-image-impl.cpp \ $(internal_src_dir)/event/images/native-image-impl.cpp \ $(internal_src_dir)/event/images/pixel-data-impl.cpp \ + $(internal_src_dir)/event/images/pixel-manipulation.cpp \ $(internal_src_dir)/event/object/custom-object-internal.cpp \ $(internal_src_dir)/event/render-tasks/render-task-impl.cpp \ $(internal_src_dir)/event/render-tasks/render-task-list-impl.cpp \ -- 2.7.4