[4.0] Fixed loading of compressed texture formats
[platform/core/uifw/dali-adaptor.git] / adaptors / common / pixel-buffer-impl.cpp
index a2b1610..b5b5ca4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2018 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,6 +25,9 @@
 // INTERNAL INCLUDES
 #include "pixel-manipulation.h"
 #include "alpha-mask.h"
+#include "gaussian-blur.h"
+#include <platform-abstractions/portable/image-operations.h>
+#include <platform-abstractions/portable/pixel-manipulation.h>
 
 namespace Dali
 {
@@ -40,7 +43,8 @@ PixelBuffer::PixelBuffer( unsigned char* buffer,
                           unsigned int width,
                           unsigned int height,
                           Dali::Pixel::Format pixelFormat )
-: mBuffer( buffer ),
+: mMetadata(),
+  mBuffer( buffer ),
   mBufferSize( bufferSize ),
   mWidth( width ),
   mHeight( height ),
@@ -133,13 +137,40 @@ Dali::PixelData PixelBuffer::CreatePixelData() const
   return pixelData;
 }
 
-void PixelBuffer::ApplyMask( const PixelBuffer& mask )
+void PixelBuffer::ApplyMask( const PixelBuffer& inMask, float contentScale, bool cropToMask )
+{
+  if( cropToMask )
+  {
+    // First scale this buffer by the contentScale, and crop to the mask size
+    // If it's too small, then scale the mask to match the image size
+    // Then apply the mask
+    ScaleAndCrop( contentScale, ImageDimensions( inMask.GetWidth(), inMask.GetHeight() ) );
+
+    if( inMask.mWidth > mWidth || inMask.mHeight > mHeight )
+    {
+      PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
+      ApplyMaskInternal( *mask );
+    }
+    else
+    {
+      ApplyMaskInternal( inMask );
+    }
+  }
+  else
+  {
+    // First, scale the mask to match the image size,
+    // then apply the mask.
+    PixelBufferPtr mask = NewResize( inMask, ImageDimensions( mWidth, mHeight ) );
+    ApplyMaskInternal( *mask );
+  }
+}
+
+void PixelBuffer::ApplyMaskInternal( const PixelBuffer& mask )
 {
   int byteOffset=0;
   int bitMask=0;
 
   Dali::Pixel::GetAlphaOffsetAndMask(mPixelFormat, byteOffset, bitMask);
-
   if( Dali::Pixel::HasAlpha( mPixelFormat ) && bitMask == 255 )
   {
     ApplyMaskToAlphaChannel( *this, mask );
@@ -147,18 +178,24 @@ void PixelBuffer::ApplyMask( const PixelBuffer& mask )
   else
   {
     PixelBufferPtr newPixelBuffer = CreateNewMaskedBuffer( *this, mask );
-    ReleaseBuffer();
-
-    // Take ownership of new buffer
-    mBuffer = newPixelBuffer->mBuffer;
-    newPixelBuffer->mBuffer = NULL;
-    mPixelFormat = newPixelBuffer->mPixelFormat;
-    mBufferSize = newPixelBuffer->mBufferSize;
-
+    TakeOwnershipOfBuffer( *newPixelBuffer );
     // On leaving scope, newPixelBuffer will get destroyed.
   }
 }
 
+void PixelBuffer::TakeOwnershipOfBuffer( PixelBuffer& pixelBuffer )
+{
+  ReleaseBuffer();
+
+  // Take ownership of new buffer
+  mBuffer = pixelBuffer.mBuffer;
+  pixelBuffer.mBuffer = NULL;
+  mBufferSize = pixelBuffer.mBufferSize;
+  mWidth = pixelBuffer.mWidth;
+  mHeight = pixelBuffer.mHeight;
+  mPixelFormat = pixelBuffer.mPixelFormat;
+}
+
 void PixelBuffer::ReleaseBuffer()
 {
   if( mBuffer )
@@ -167,6 +204,188 @@ void PixelBuffer::ReleaseBuffer()
   }
 }
 
+void PixelBuffer::AllocateFixedSize( uint32_t size )
+{
+  ReleaseBuffer();
+  mBuffer = reinterpret_cast<unsigned char*>(malloc( size ));
+  mBufferSize = size;
+}
+
+void PixelBuffer::ScaleAndCrop( float scaleFactor, ImageDimensions cropDimensions )
+{
+  ImageDimensions outDimensions( float(mWidth) * scaleFactor,
+                                 float(mHeight) * scaleFactor );
+
+  if( outDimensions.GetWidth() != mWidth || outDimensions.GetHeight() != mHeight )
+  {
+    Resize( outDimensions );
+  }
+
+  ImageDimensions postCropDimensions(
+    std::min(cropDimensions.GetWidth(), outDimensions.GetWidth()),
+    std::min(cropDimensions.GetHeight(), outDimensions.GetHeight()));
+
+  if( postCropDimensions.GetWidth() < outDimensions.GetWidth() ||
+      postCropDimensions.GetHeight() < outDimensions.GetHeight() )
+  {
+    uint16_t x = ( outDimensions.GetWidth()  - postCropDimensions.GetWidth() ) / 2;
+    uint16_t y = ( outDimensions.GetHeight() - postCropDimensions.GetHeight() ) / 2;
+    Crop( x, y, postCropDimensions );
+  }
+}
+
+void PixelBuffer::Crop( uint16_t x, uint16_t y, ImageDimensions cropDimensions )
+{
+  PixelBufferPtr outBuffer = NewCrop( *this, x, y, cropDimensions );
+  TakeOwnershipOfBuffer( *outBuffer );
+}
+
+PixelBufferPtr PixelBuffer::NewCrop( const PixelBuffer& inBuffer, uint16_t x, uint16_t y, ImageDimensions cropDimensions )
+{
+  PixelBufferPtr outBuffer = PixelBuffer::New( cropDimensions.GetWidth(), cropDimensions.GetHeight(), inBuffer.GetPixelFormat() );
+  int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
+  int srcStride = inBuffer.mWidth * bytesPerPixel;
+  int destStride = cropDimensions.GetWidth() * bytesPerPixel;
+
+  // Clamp crop to right edge
+  if( x + cropDimensions.GetWidth() > inBuffer.mWidth )
+  {
+    destStride = ( inBuffer.mWidth - x ) * bytesPerPixel;
+  }
+
+  int srcOffset = x * bytesPerPixel + y * srcStride;
+  int destOffset = 0;
+  unsigned char* destBuffer = outBuffer->mBuffer;
+
+  // Clamp crop to last row
+  unsigned int endRow = y + cropDimensions.GetHeight();
+  if( endRow > inBuffer.mHeight )
+  {
+    endRow = inBuffer.mHeight - 1 ;
+  }
+  for( uint16_t row = y; row < endRow; ++row )
+  {
+    memcpy(destBuffer + destOffset, inBuffer.mBuffer + srcOffset, destStride );
+    srcOffset += srcStride;
+    destOffset += destStride;
+  }
+  return outBuffer;
+
+}
+
+void PixelBuffer::SetMetadata( const Property::Map& map )
+{
+  mMetadata.reset(new Property::Map(map));
+}
+
+bool PixelBuffer::GetMetadata(Property::Map& outMetadata) const
+{
+  if( !mMetadata )
+  {
+    return false;
+  }
+  outMetadata = *mMetadata;
+  return true;
+}
+
+void PixelBuffer::SetMetadata(std::unique_ptr<Property::Map> metadata)
+{
+  mMetadata = std::move(metadata);
+}
+
+void PixelBuffer::Resize( ImageDimensions outDimensions )
+{
+  if( mWidth != outDimensions.GetWidth() || mHeight != outDimensions.GetHeight() )
+  {
+    PixelBufferPtr outBuffer = NewResize( *this, outDimensions );
+    TakeOwnershipOfBuffer( *outBuffer );
+  }
+}
+
+PixelBufferPtr PixelBuffer::NewResize( const PixelBuffer& inBuffer, ImageDimensions outDimensions )
+{
+  PixelBufferPtr outBuffer = PixelBuffer::New( outDimensions.GetWidth(), outDimensions.GetHeight(), inBuffer.GetPixelFormat() );
+  ImageDimensions inDimensions( inBuffer.mWidth, inBuffer.mHeight );
+
+  bool hasAlpha = Pixel::HasAlpha( inBuffer.mPixelFormat );
+  int bytesPerPixel = Pixel::GetBytesPerPixel( inBuffer.mPixelFormat );
+
+  Resampler::Filter filterType = Resampler::LANCZOS4;
+  if( inDimensions.GetWidth() < outDimensions.GetWidth() && inDimensions.GetHeight() < outDimensions.GetHeight() )
+  {
+    filterType = Resampler::MITCHELL;
+  }
+
+  // This method only really works for 8 bit wide channels.
+  // (But could be expanded to work)
+  if( inBuffer.mPixelFormat == Pixel::A8 ||
+      inBuffer.mPixelFormat == Pixel::L8 ||
+      inBuffer.mPixelFormat == Pixel::LA88 ||
+      inBuffer.mPixelFormat == Pixel::RGB888 ||
+      inBuffer.mPixelFormat == Pixel::RGB8888 ||
+      inBuffer.mPixelFormat == Pixel::BGR8888 ||
+      inBuffer.mPixelFormat == Pixel::RGBA8888 ||
+      inBuffer.mPixelFormat == Pixel::BGRA8888 )
+  {
+    Dali::Internal::Platform::Resample( inBuffer.mBuffer, inDimensions,
+                                        outBuffer->GetBuffer(), outDimensions,
+                                        filterType, bytesPerPixel, hasAlpha );
+  }
+  else
+  {
+    DALI_LOG_ERROR( "Trying to resize an image with too narrow a channel width" );
+  }
+
+  return outBuffer;
+}
+
+void PixelBuffer::ApplyGaussianBlur( const float blurRadius )
+{
+  // This method only works for pixel buffer in RGBA format.
+  if( mWidth > 0 && mHeight > 0 && mPixelFormat == Pixel::RGBA8888 )
+  {
+    if ( blurRadius > Math::MACHINE_EPSILON_1 )
+    {
+      PerformGaussianBlurRGBA( *this, blurRadius );
+    }
+  }
+  else
+  {
+    DALI_LOG_ERROR( "Trying to apply gaussian blur to an empty pixel buffer or a pixel buffer not in RGBA format" );
+  }
+}
+
+void PixelBuffer::MultiplyColorByAlpha()
+{
+  auto bytesPerPixel = Pixel::GetBytesPerPixel( mPixelFormat );
+
+  // Compressed textures have unknown size of the pixel. Alpha premultiplication
+  // must be skipped in such case
+  if( Pixel::GetBytesPerPixel(mPixelFormat) && Pixel::HasAlpha(mPixelFormat) )
+  {
+    unsigned char* pixel = mBuffer;
+    const unsigned int bufferSize = mWidth * mHeight;
+
+    for( unsigned int i=0; i<bufferSize; ++i )
+    {
+      unsigned int alpha = ReadChannel( pixel, mPixelFormat, Adaptor::ALPHA );
+      {
+        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 );
+      }
+      pixel += bytesPerPixel;
+    }
+  }
+}
+
+
+
 
 }// namespace Adaptor
 }// namespace Internal