Added missing newline chars to logging commands
[platform/core/uifw/dali-core.git] / dali / internal / render / gl-resources / bitmap-texture.cpp
index 0900dc6..58d80c2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 #include <dali/internal/render/gl-resources/bitmap-texture.h>
 
 // EXTERNAL INCLUDES
-#include <math.h>
-#include <memory.h>
+#include <cstring>
 
 // INTERNAL INCLUDES
-#include <dali/public-api/math/rect.h>
-#include <dali/public-api/math/math-utils.h>
+#include <dali/public-api/common/dali-vector.h>
+#include <dali/public-api/images/buffer-image.h> // For PixelBuffer
 #include <dali/integration-api/debug.h>
-#include <dali/internal/render/common/vertex.h>
 #include <dali/internal/render/common/performance-monitor.h>
 #include <dali/internal/render/gl-resources/context.h>
+#include <dali/internal/render/gl-resources/texture-units.h>
+
 
 namespace Dali
 {
@@ -36,14 +36,19 @@ namespace Dali
 namespace Internal
 {
 
-BitmapTexture::BitmapTexture(Integration::Bitmap* const bitmap, const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixelsProfile, Context& context)
-: Texture(context,
-          bitmapPackedPixelsProfile->GetBufferWidth(),
-          bitmapPackedPixelsProfile->GetBufferHeight(),
-          bitmap->GetImageWidth(),
-          bitmap->GetImageHeight(),
-          bitmap->GetPixelFormat()),
+BitmapTexture::BitmapTexture(
+  Integration::Bitmap* const bitmap,
+  const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixelsProfile,
+  Context& context,
+  ResourcePolicy::Discardable policy)
+: Texture( context,
+           bitmapPackedPixelsProfile->GetBufferWidth(),
+           bitmapPackedPixelsProfile->GetBufferHeight(),
+           bitmap->GetImageWidth(),
+           bitmap->GetImageHeight()),
   mBitmap(bitmap),
+  mPixelFormat(bitmap->GetPixelFormat()),
+  mDiscardPolicy(policy),
   mClearPixels(false)
 {
   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
@@ -51,16 +56,18 @@ BitmapTexture::BitmapTexture(Integration::Bitmap* const bitmap, const Integratio
 }
 
 BitmapTexture::BitmapTexture(
-  unsigned int  width,
-  unsigned int  height,
+  unsigned int width,
+  unsigned int height,
   Pixel::Format pixelFormat,
-  bool          clearPixels,
-  Context&      context)
-: Texture(context,
-          width, height,
-          width, height,
-          pixelFormat),
+  bool clearPixels,
+  Context& context,
+  ResourcePolicy::Discardable policy)
+: Texture( context,
+           width, height,
+           width, height),
   mBitmap(NULL),
+  mPixelFormat( pixelFormat ),
+  mDiscardPolicy(policy),
   mClearPixels(clearPixels)
 {
   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
@@ -69,64 +76,11 @@ BitmapTexture::BitmapTexture(
 BitmapTexture::~BitmapTexture()
 {
   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
+
   // GlCleanup() should already have been called by TextureCache ensuring the resource is destroyed
   // on the render thread. (And avoiding a potentially problematic virtual call in the destructor)
 }
 
-void BitmapTexture::UploadBitmapArray( const BitmapUploadArray& bitmapArray )
-{
-  if( mId == 0 )
-  {
-    CreateGlTexture();
-  }
-
-  mContext.ActiveTexture(GL_TEXTURE7);  // bind in unused unit so rebind works the first time
-  mContext.Bind2dTexture(mId);
-  mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
-
-  GLenum pixelFormat   = GL_RGBA;
-  GLenum pixelDataType = GL_UNSIGNED_BYTE;
-
-  Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
-
-  // go through each bitmap uploading it
-
-  for( BitmapUploadArray::const_iterator iter =  bitmapArray.begin(), endIter = bitmapArray.end(); iter != endIter ; ++iter)
-  {
-    const BitmapUpload& bitmapItem((*iter));
-
-    DALI_ASSERT_ALWAYS(bitmapItem.mPixelData);
-
-    const unsigned char* pixels = bitmapItem.mPixelData;
-
-    DALI_ASSERT_DEBUG( (pixels!=NULL) && "bitmap has no data \n");
-
-    unsigned int bitmapWidth(bitmapItem.mWidth);
-    unsigned int bitmapHeight(bitmapItem.mHeight);
-
-    DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "upload bitmap to texture :%d y:%d w:%d h:%d\n",
-                            bitmapItem.mXpos,
-                            bitmapItem.mYpos,
-                            bitmapWidth,
-                            bitmapHeight);
-
-     mContext.TexSubImage2D(GL_TEXTURE_2D,
-                            0,                   /* mip map level */
-                            bitmapItem.mXpos,    /* X pos */
-                            bitmapItem.mYpos,    /* Y pos */
-                            bitmapWidth,         /* width */
-                            bitmapHeight,        /* height */
-                            pixelFormat,         /* our bitmap format (should match internal format) */
-                            pixelDataType,       /* pixel data type */
-                            pixels);             /* texture data */
-
-     if( BitmapUpload::DISCARD_PIXEL_DATA == bitmapItem.mDiscard)
-     {
-       delete [] bitmapItem.mPixelData;
-     }
-  }
-}
-
 bool BitmapTexture::HasAlphaChannel() const
 {
   return Pixel::HasAlpha(mPixelFormat);
@@ -154,7 +108,7 @@ void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char
   GLenum pixelDataType = GL_UNSIGNED_BYTE;
   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
 
-  mContext.ActiveTexture(GL_TEXTURE7);  // bind in unused unit so rebind works the first time
+  mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
 
   mContext.Bind2dTexture(mId);
 
@@ -164,9 +118,8 @@ void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char
     DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%d y:%d w:%d h:%d\n",
                    updateArea.x, updateArea.y, updateArea.width ,updateArea.height );
 
-    // TODO: obtain pitch of source image, obtain pixel depth of source image.
-    const unsigned int pitchPixels = mImageWidth;
-    const unsigned int pixelDepth = sizeof(unsigned int);
+    const unsigned int pitchPixels = mWidth;
+    const unsigned int pixelDepth = Dali::Pixel::GetBytesPerPixel( mPixelFormat );
 
     // If the width of the source update area is the same as the pitch, then can
     // copy the contents in a single contiguous TexSubImage call.
@@ -198,7 +151,6 @@ void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char
   }
 }
 
-
 void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pixels )
 {
   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
@@ -213,7 +165,7 @@ void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pix
   }
   DALI_ASSERT_DEBUG( mId != 0 );
 
-  mContext.ActiveTexture(GL_TEXTURE7); // bind in unused unit so rebind works the first time
+  mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
   mContext.Bind2dTexture(mId);
   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
 
@@ -222,7 +174,13 @@ void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pix
   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-  INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, GetBytesPerPixel(mPixelFormat) * mWidth * mHeight );
+  // If the resource policy is to discard on upload then release buffer
+  DiscardBitmapBuffer();
+
+  if( pixels != NULL )
+  {
+    INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, GetBytesPerPixel(mPixelFormat) * mWidth * mHeight );
+  }
 }
 
 void BitmapTexture::Update( Integration::Bitmap* bitmap )
@@ -231,7 +189,7 @@ void BitmapTexture::Update( Integration::Bitmap* bitmap )
   DALI_ASSERT_DEBUG( bitmap != 0 );
   if( !bitmap )
   {
-    DALI_LOG_ERROR( "Passed a null bitmap to update this bitmap texture." );
+    DALI_LOG_ERROR( "Passed a null bitmap to update this bitmap texture.\n" );
     return;
   }
 
@@ -241,21 +199,22 @@ void BitmapTexture::Update( Integration::Bitmap* bitmap )
   if( !bitmapPackedPixels )
   {
     ///! This should never happen.
-    DALI_LOG_ERROR("Passed an incompatible bitmap type to update this bitmap texture.");
+    DALI_LOG_ERROR("Passed an incompatible bitmap type to update this bitmap texture.\n");
     return;
   }
   mBitmap = bitmap;
 
   const unsigned char* pixels = mBitmap->GetBuffer();
 
+  // We should never have null pixel data here - resource manager has deliberately loaded/reloaded data
+
   DALI_ASSERT_DEBUG( pixels != NULL );
 
-  if ( NULL == pixels )
+  if( NULL == pixels )
   {
-    DALI_LOG_ERROR("bitmap has no data\n");
-    GlCleanup(); // Note, We suicide here in the case of bad input.
+    DALI_LOG_ERROR("BitmapTexture::Upload() - Bitmap has no pixel data.\n");
   }
-  else
+  else if( mId != 0 )
   {
     if( mImageWidth == mBitmap->GetImageWidth() &&
         mImageHeight == mBitmap->GetImageHeight() &&
@@ -263,28 +222,103 @@ void BitmapTexture::Update( Integration::Bitmap* bitmap )
         mHeight == bitmapPackedPixels->GetBufferHeight() &&
         mPixelFormat == mBitmap->GetPixelFormat() ) // and size hasn't changed
     {
-      if ( mId ) // If the texture is already bound
-      {
-        RectArea area(0, 0, mImageWidth, mImageHeight);  // just update whole texture
-        AreaUpdated( area, pixels );
-        mBitmap->DiscardBuffer();
-      }
+      RectArea area(0, 0, mImageWidth, mImageHeight);  // just update whole texture
+      AreaUpdated( area, pixels );
+      DiscardBitmapBuffer();
     }
-    else
-    {                                           // Otherwise, reload the pixel data
+    else // Otherwise, reload the pixel data
+    {
       mImageWidth  = mBitmap->GetImageWidth();
       mImageHeight = mBitmap->GetImageHeight();
       mWidth       = bitmapPackedPixels->GetBufferWidth();
       mHeight      = bitmapPackedPixels->GetBufferHeight();
       mPixelFormat = mBitmap->GetPixelFormat();
 
-      if ( mId ) // If the texture is already bound
-      {
-        AssignBitmap( false, pixels );
-        mBitmap->DiscardBuffer();
-      }
+      AssignBitmap( false, pixels );
+    }
+  }
+}
+
+void BitmapTexture::Update( Integration::Bitmap* srcBitmap, std::size_t xOffset, std::size_t yOffset )
+{
+  if( NULL != srcBitmap )
+  {
+    Update( srcBitmap->GetBuffer(), srcBitmap->GetImageWidth(), srcBitmap->GetImageHeight(),
+            srcBitmap->GetPixelFormat(), xOffset, yOffset);
+  }
+}
+
+void BitmapTexture::Update( PixelData* srcPixelData, std::size_t xOffset, std::size_t yOffset )
+{
+  if( NULL != srcPixelData )
+  {
+    Update( srcPixelData->GetBuffer(), srcPixelData->GetWidth(), srcPixelData->GetHeight(),
+            srcPixelData->GetPixelFormat(), xOffset, yOffset);
+  }
+}
+
+void BitmapTexture::Update( const unsigned char* pixels, std::size_t width, std::size_t height, Pixel::Format pixelFormat, std::size_t xOffset, std::size_t yOffset )
+{
+
+  GLenum pixelGLFormat = GL_RGBA;
+  GLenum pixelDataType = GL_UNSIGNED_BYTE;
+  Integration::ConvertToGlFormat( mPixelFormat, pixelDataType, pixelGLFormat );
+
+  if( !mId )
+  {
+    mContext.GenTextures( 1, &mId );
+
+    mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
+    mContext.Bind2dTexture( mId );
+    mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+
+    mContext.TexImage2D( GL_TEXTURE_2D, 0, pixelGLFormat, mWidth, mHeight, 0, pixelGLFormat, pixelDataType, NULL );
+    mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
+    mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
+  }
+  else
+  {
+    mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
+    mContext.Bind2dTexture( mId );
+    mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
+  }
+
+#if DALI_GLES_VERSION >= 30
+  // For GLES 3.0, uploading sub-image with different format is a valid operation.
+  Integration::ConvertToGlFormat( pixelFormat, pixelDataType, pixelGLFormat );
+#else
+  // Allows RGB888 source bitmap to be added to RGBA8888 texture, need to convert the bitmap format manually.
+  if(pixelFormat == Pixel::RGB888 && mPixelFormat == Pixel::RGBA8888 )
+  {
+    std::size_t size = width * height;
+
+    Vector<PixelBuffer> tempBuffer;
+    tempBuffer.Reserve( size*4u );
+    PixelBuffer* data = tempBuffer.Begin();
+
+    for( std::size_t i=0u; i<size; i++ )
+    {
+      data[i*4u] = pixels[i*3u];
+      data[i*4u+1] = pixels[i*3u+1];
+      data[i*4u+2] = pixels[i*3u+2];
+      data[i*4u+3] = 0xFF;
     }
+
+    mContext.TexSubImage2D( GL_TEXTURE_2D, 0,
+                            xOffset, yOffset,
+                            width, height,
+                            pixelGLFormat, pixelDataType,
+                            data );
+
+    return;
   }
+#endif
+
+  mContext.TexSubImage2D( GL_TEXTURE_2D, 0,
+                          xOffset, yOffset,
+                          width, height,
+                          pixelGLFormat, pixelDataType,
+                          pixels );
 }
 
 void BitmapTexture::UpdateArea( const RectArea& updateArea )
@@ -294,14 +328,12 @@ void BitmapTexture::UpdateArea( const RectArea& updateArea )
   if( mBitmap != 0 )
   {
     const unsigned char* pixels = mBitmap->GetBuffer();
-    if ( NULL == pixels )
-    {
-      DALI_LOG_ERROR("bitmap has no data\n");
-      GlCleanup(); ///!ToDo: Why do we suicide in the case of bad input?
-    }
-    else
+
+    // Pixel data could be null if we've uploaded to GL and discarded the data.
+
+    if( NULL != pixels )
     {
-      if ( mId ) // If the texture is already bound
+      if( mId ) // If the texture is already bound
       {
         if( updateArea.IsEmpty() )
         {
@@ -321,53 +353,6 @@ void BitmapTexture::UpdateArea( const RectArea& updateArea )
   }
 }
 
-void BitmapTexture::ClearAreas( const BitmapClearArray& areaArray, std::size_t blockSize, uint32_t color )
-{
-  if(mId > 0)
-  {
-    DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
-    DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
-
-    GLenum pixelFormat   = GL_RGBA;
-    GLenum pixelDataType = GL_UNSIGNED_BYTE;
-    Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
-
-    mContext.ActiveTexture(GL_TEXTURE7);  // bind in unused unit so rebind works the first time
-    mContext.Bind2dTexture(mId);
-
-    size_t numPixels = blockSize*blockSize;
-    size_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
-    char* clearPixels = (char*)malloc(numPixels * bytesPerPixel);
-
-    for(size_t i=0; i<numPixels; i++)
-    {
-      memcpy(&clearPixels[i*bytesPerPixel], &color, bytesPerPixel);
-    }
-
-    for( BitmapClearArray::const_iterator iter =  areaArray.begin(), endIter = areaArray.end(); iter != endIter ; ++iter)
-    {
-      const Vector2& clearPos((*iter));
-
-      mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
-      DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%0.2f y:%0.2f w:%d h:%d\n",
-                     clearPos.x, clearPos.y, blockSize, blockSize );
-
-      mContext.TexSubImage2D(GL_TEXTURE_2D,
-                             0,
-                             clearPos.x,
-                             clearPos.y,
-                             blockSize,
-                             blockSize,
-                             pixelFormat,         /* our bitmap format (should match internal format) */
-                             pixelDataType,       /* pixel data type */
-                             clearPixels);        /* texture data */
-
-      INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, numPixels * bytesPerPixel );
-    }
-    free(clearPixels);
-  }
-}
-
 bool BitmapTexture::UpdateOnCreate()
 {
   return true;
@@ -376,28 +361,31 @@ bool BitmapTexture::UpdateOnCreate()
 bool BitmapTexture::CreateGlTexture()
 {
   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
-  DALI_LOG_INFO(Debug::Filter::gImage, Debug::Verbose, "Bitmap: %s\n", DALI_LOG_GET_OBJECT_C_STR(this));
+  DALI_LOG_INFO(Debug::Filter::gImage, Debug::Verbose, "BitmapTexture::CreateGlTexture() Bitmap: %s\n", DALI_LOG_GET_OBJECT_C_STR(this));
 
   if( mBitmap )
   {
     const unsigned char* pixels = mBitmap->GetBuffer();
 
-    DALI_ASSERT_DEBUG(pixels != NULL);
+    // pixel data could be NULL here if we've had a context loss and we previously discarded
+    // the pixel data on the previous upload. If it is null, then we shouldn't generate a
+    // new GL Texture; leaving mId as zero. Eventually, the bitmap will get reloaded,
+    // and pixels will become non-null.
 
     if( NULL != pixels )
     {
       AssignBitmap( true, pixels );
-      mBitmap->DiscardBuffer();
+      DiscardBitmapBuffer();
     }
   }
   else
   {
     const unsigned char* pixels = NULL;
-    std::vector<unsigned char> pixelData;
-    if( ( NULL == pixels ) && ( true == mClearPixels ) )
+    Dali::Vector<unsigned char> pixelData; // Okay to create outside branch as empty vector has no heap allocation.
+    if( true == mClearPixels )
     {
-      unsigned int size = mWidth * mHeight * Pixel::GetBytesPerPixel(mPixelFormat);
-      pixelData.resize(size, 0);
+      unsigned int size = mWidth * mHeight * Pixel::GetBytesPerPixel( mPixelFormat );
+      pixelData.Resize( size, 0 );
       pixels = &pixelData[0];
     }
     AssignBitmap( true, pixels );
@@ -433,6 +421,21 @@ unsigned int BitmapTexture::GetHeight() const
   return height;
 }
 
+void BitmapTexture::DiscardBitmapBuffer()
+{
+  DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "BitmapTexture::DiscardBitmapBuffer() DiscardPolicy: %s\n", mDiscardPolicy == ResourcePolicy::OWNED_DISCARD?"DISCARD":"RETAIN");
+
+  if( ResourcePolicy::OWNED_DISCARD == mDiscardPolicy )
+  {
+    DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "  Discarding bitmap\n");
+    if ( mBitmap )
+    {
+      mBitmap->DiscardBuffer();
+    }
+  }
+}
+
+
 } //namespace Internal
 
 } //namespace Dali