2 // Copyright (c) 2014 Samsung Electronics Co., Ltd.
4 // Licensed under the Flora License, Version 1.0 (the License);
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://floralicense.org/license/
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an AS IS BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
18 #include <dali/internal/render/gl-resources/bitmap-texture.h>
25 #include <dali/public-api/math/rect.h>
26 #include <dali/public-api/math/math-utils.h>
27 #include <dali/integration-api/debug.h>
28 #include <dali/internal/render/common/vertex.h>
29 #include <dali/internal/render/common/performance-monitor.h>
30 #include <dali/internal/render/gl-resources/context.h>
38 BitmapTexture::BitmapTexture(Integration::Bitmap* const bitmap, const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixelsProfile, Context& context)
40 bitmapPackedPixelsProfile->GetBufferWidth(),
41 bitmapPackedPixelsProfile->GetBufferHeight(),
42 bitmap->GetImageWidth(),
43 bitmap->GetImageHeight(),
44 bitmap->GetPixelFormat()),
48 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
49 DALI_LOG_SET_OBJECT_STRING(this, DALI_LOG_GET_OBJECT_STRING(bitmap));
52 BitmapTexture::BitmapTexture(
55 Pixel::Format pixelFormat,
63 mClearPixels(clearPixels)
65 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
68 BitmapTexture::~BitmapTexture()
70 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
71 // GlCleanup() should already have been called by TextureCache ensuring the resource is destroyed
72 // on the render thread. (And avoiding a potentially problematic virtual call in the destructor)
75 void BitmapTexture::UploadBitmapArray( const BitmapUploadArray& bitmapArray )
82 mContext.ActiveTexture(GL_TEXTURE7); // bind in unused unit so rebind works the first time
83 mContext.Bind2dTexture(mId);
84 mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
86 GLenum pixelFormat = GL_RGBA;
87 GLenum pixelDataType = GL_UNSIGNED_BYTE;
89 Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
91 // go through each bitmap uploading it
93 for( BitmapUploadArray::const_iterator iter = bitmapArray.begin(), endIter = bitmapArray.end(); iter != endIter ; ++iter)
95 const BitmapUpload& bitmapItem((*iter));
97 DALI_ASSERT_ALWAYS(bitmapItem.mPixelData);
99 const unsigned char* pixels = bitmapItem.mPixelData;
101 DALI_ASSERT_DEBUG( (pixels!=NULL) && "bitmap has no data \n");
103 unsigned int bitmapWidth(bitmapItem.mWidth);
104 unsigned int bitmapHeight(bitmapItem.mHeight);
106 DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "upload bitmap to texture :%d y:%d w:%d h:%d\n",
112 mContext.TexSubImage2D(GL_TEXTURE_2D,
113 0, /* mip map level */
114 bitmapItem.mXpos, /* X pos */
115 bitmapItem.mYpos, /* Y pos */
116 bitmapWidth, /* width */
117 bitmapHeight, /* height */
118 pixelFormat, /* our bitmap format (should match internal format) */
119 pixelDataType, /* pixel data type */
120 pixels); /* texture data */
122 if( BitmapUpload::DISCARD_PIXEL_DATA == bitmapItem.mDiscard)
124 delete [] bitmapItem.mPixelData;
129 bool BitmapTexture::HasAlphaChannel() const
131 return Pixel::HasAlpha(mPixelFormat);
134 bool BitmapTexture::IsFullyOpaque() const
138 return mBitmap->IsFullyOpaque();
142 return ! HasAlphaChannel(); // Todo: amalgamate updated bitmap's IsFullyOpaque()
146 // Bitmap buffer has been changed. Upload changes to GPU.
147 void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char* pixels )
149 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
150 DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
152 GLenum pixelFormat = GL_RGBA;
153 GLenum pixelDataType = GL_UNSIGNED_BYTE;
154 Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
156 mContext.ActiveTexture(GL_TEXTURE7); // bind in unused unit so rebind works the first time
158 mContext.Bind2dTexture(mId);
160 if( ! updateArea.IsEmpty() )
162 mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
163 DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%d y:%d w:%d h:%d\n",
164 updateArea.x, updateArea.y, updateArea.width ,updateArea.height );
166 // TODO: obtain pitch of source image, obtain pixel depth of source image.
167 const unsigned int pitchPixels = mImageWidth;
168 const unsigned int pixelDepth = sizeof(unsigned int);
170 // If the width of the source update area is the same as the pitch, then can
171 // copy the contents in a single contiguous TexSubImage call.
172 if(updateArea.x == 0 && updateArea.width == pitchPixels)
174 pixels += updateArea.y * pitchPixels * pixelDepth;
175 mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, updateArea.y,
176 updateArea.width, updateArea.height,
177 pixelFormat, pixelDataType, pixels );
181 // Otherwise the source buffer needs to be copied line at a time, as OpenGL ES
182 // does not support source strides. (no GL_UNPACK_ROW_LENGTH supported)
183 unsigned int yBottom = updateArea.y + updateArea.height;
184 pixels += (updateArea.y * pitchPixels + updateArea.x) * pixelDepth;
186 for(unsigned int y = updateArea.y; y < yBottom; y++)
188 mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, y,
190 pixelFormat, pixelDataType, pixels );
191 pixels += pitchPixels * pixelDepth;
195 INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED,
196 updateArea.Area()* GetBytesPerPixel( mPixelFormat ));
201 void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pixels )
203 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
204 DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AssignBitmap()\n");
206 GLenum pixelFormat = GL_RGBA;
207 GLenum pixelDataType = GL_UNSIGNED_BYTE;
209 if( generateTexture )
211 mContext.GenTextures(1, &mId);
213 DALI_ASSERT_DEBUG( mId != 0 );
215 mContext.ActiveTexture(GL_TEXTURE7); // bind in unused unit so rebind works the first time
216 mContext.Bind2dTexture(mId);
217 Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
219 mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
220 mContext.TexImage2D(GL_TEXTURE_2D, 0, pixelFormat, mWidth, mHeight, 0, pixelFormat, pixelDataType, pixels);
221 mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
222 mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
223 mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
224 mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
226 DALI_LOG_RESOURCE("[UPLOAD] Uploaded image data from Bitmap %p to Texture %d - size %d bytes (%dx%d)\n",
227 mBitmap.Get(), mId, Pixel::GetBytesPerPixel(mPixelFormat)*mWidth*mHeight, mWidth, mHeight);
228 INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, GetBytesPerPixel(mPixelFormat) * mWidth * mHeight );
231 void BitmapTexture::Update( Integration::Bitmap* bitmap )
233 DALI_LOG_INFO( Debug::Filter::gGLResource, Debug::General, "BitmapTexture::Update(bitmap:%p )\n", bitmap );
234 DALI_ASSERT_DEBUG( bitmap != 0 );
237 DALI_LOG_ERROR( "Passed a null bitmap to update this bitmap texture." );
241 // Only Packed-pixel bitmaps are ever associated with BitmapTextures, so we should never be passed any other kind:
242 const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixels = bitmap->GetPackedPixelsProfile();
243 DALI_ASSERT_DEBUG(bitmapPackedPixels);
244 if( !bitmapPackedPixels )
246 ///! This should never happen.
247 DALI_LOG_ERROR("Passed an incompatible bitmap type to update this bitmap texture.");
252 const unsigned char* pixels = mBitmap->GetBuffer();
254 DALI_ASSERT_DEBUG( pixels != NULL );
256 if ( NULL == pixels )
258 DALI_LOG_ERROR("bitmap has no data\n");
259 GlCleanup(); // Note, We suicide here in the case of bad input.
263 if( mImageWidth == mBitmap->GetImageWidth() &&
264 mImageHeight == mBitmap->GetImageHeight() &&
265 mWidth == bitmapPackedPixels->GetBufferWidth() &&
266 mHeight == bitmapPackedPixels->GetBufferHeight() &&
267 mPixelFormat == mBitmap->GetPixelFormat() ) // and size hasn't changed
269 if ( mId ) // If the texture is already bound
271 RectArea area(0, 0, mImageWidth, mImageHeight); // just update whole texture
272 AreaUpdated( area, pixels );
273 mBitmap->DiscardBuffer();
277 { // Otherwise, reload the pixel data
278 mImageWidth = mBitmap->GetImageWidth();
279 mImageHeight = mBitmap->GetImageHeight();
280 mWidth = bitmapPackedPixels->GetBufferWidth();
281 mHeight = bitmapPackedPixels->GetBufferHeight();
282 mPixelFormat = mBitmap->GetPixelFormat();
284 if ( mId ) // If the texture is already bound
286 AssignBitmap( false, pixels );
287 mBitmap->DiscardBuffer();
293 void BitmapTexture::UpdateArea( const RectArea& updateArea )
295 DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "BitmapTexture::UpdateArea()\n");
299 const unsigned char* pixels = mBitmap->GetBuffer();
300 if ( NULL == pixels )
302 DALI_LOG_ERROR("bitmap has no data\n");
303 GlCleanup(); ///!ToDo: Why do we suicide in the case of bad input?
307 if ( mId ) // If the texture is already bound
309 if( updateArea.IsEmpty() )
314 area.width = mImageWidth;
315 area.height = mImageHeight;
316 AreaUpdated( area, pixels );
320 AreaUpdated( updateArea, pixels );
327 void BitmapTexture::ClearAreas( const BitmapClearArray& areaArray, std::size_t blockSize, uint32_t color )
331 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
332 DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
334 GLenum pixelFormat = GL_RGBA;
335 GLenum pixelDataType = GL_UNSIGNED_BYTE;
336 Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
338 mContext.ActiveTexture(GL_TEXTURE7); // bind in unused unit so rebind works the first time
339 mContext.Bind2dTexture(mId);
341 size_t numPixels = blockSize*blockSize;
342 size_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
343 char* clearPixels = (char*)malloc(numPixels * bytesPerPixel);
345 for(size_t i=0; i<numPixels; i++)
347 memcpy(&clearPixels[i*bytesPerPixel], &color, bytesPerPixel);
350 for( BitmapClearArray::const_iterator iter = areaArray.begin(), endIter = areaArray.end(); iter != endIter ; ++iter)
352 const Vector2& clearPos((*iter));
354 mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
355 DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%0.2f y:%0.2f w:%d h:%d\n",
356 clearPos.x, clearPos.y, blockSize, blockSize );
358 mContext.TexSubImage2D(GL_TEXTURE_2D,
364 pixelFormat, /* our bitmap format (should match internal format) */
365 pixelDataType, /* pixel data type */
366 clearPixels); /* texture data */
368 INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, numPixels * bytesPerPixel );
374 bool BitmapTexture::UpdateOnCreate()
379 bool BitmapTexture::CreateGlTexture()
381 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
382 DALI_LOG_INFO(Debug::Filter::gImage, Debug::Verbose, "Bitmap: %s\n", DALI_LOG_GET_OBJECT_C_STR(this));
386 const unsigned char* pixels = mBitmap->GetBuffer();
388 DALI_ASSERT_DEBUG(pixels != NULL);
392 AssignBitmap( true, pixels );
393 mBitmap->DiscardBuffer();
398 const unsigned char* pixels = NULL;
399 std::vector<unsigned char> pixelData;
400 if( ( NULL == pixels ) && ( true == mClearPixels ) )
402 unsigned int size = mWidth * mHeight * Pixel::GetBytesPerPixel(mPixelFormat);
403 pixelData.resize(size, 0);
404 pixels = &pixelData[0];
406 AssignBitmap( true, pixels );
412 bool BitmapTexture::Init()
414 DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
415 // mBitmap should be initialized by now
416 return (mBitmap != 0);
419 unsigned int BitmapTexture::GetWidth() const
421 unsigned int width = mWidth;
424 width = mBitmap->GetImageWidth();
429 unsigned int BitmapTexture::GetHeight() const
431 unsigned int height = mHeight;
434 height = mBitmap->GetImageHeight();
439 } //namespace Internal