(Atlas) Upload pixel data & Allow the difference in pixel format
[platform/core/uifw/dali-core.git] / dali / internal / render / gl-resources / bitmap-texture.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali/internal/render/gl-resources/bitmap-texture.h>
20
21 // INTERNAL INCLUDES
22 #include <dali/public-api/common/dali-vector.h>
23 #include <dali/integration-api/debug.h>
24 #include <dali/internal/render/common/vertex.h>
25 #include <dali/internal/render/common/performance-monitor.h>
26 #include <dali/internal/render/gl-resources/context.h>
27 #include <dali/internal/render/gl-resources/texture-units.h>
28
29
30 namespace Dali
31 {
32
33 namespace Internal
34 {
35
36 BitmapTexture::BitmapTexture(
37   Integration::Bitmap* const bitmap,
38   const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixelsProfile,
39   Context& context,
40   ResourcePolicy::Discardable policy)
41 : Texture( context,
42            bitmapPackedPixelsProfile->GetBufferWidth(),
43            bitmapPackedPixelsProfile->GetBufferHeight(),
44            bitmap->GetImageWidth(),
45            bitmap->GetImageHeight()),
46   mBitmap(bitmap),
47   mClearPixels(false),
48   mDiscardPolicy(policy),
49   mPixelFormat(bitmap->GetPixelFormat())
50 {
51   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
52   DALI_LOG_SET_OBJECT_STRING(this, DALI_LOG_GET_OBJECT_STRING(bitmap));
53 }
54
55 BitmapTexture::BitmapTexture(
56   unsigned int width,
57   unsigned int height,
58   Pixel::Format pixelFormat,
59   bool clearPixels,
60   Context& context,
61   ResourcePolicy::Discardable policy)
62 : Texture( context,
63            width, height,
64            width, height),
65   mBitmap(NULL),
66   mClearPixels(clearPixels),
67   mDiscardPolicy(policy),
68   mPixelFormat( pixelFormat )
69 {
70   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
71 }
72
73 BitmapTexture::~BitmapTexture()
74 {
75   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
76
77   // GlCleanup() should already have been called by TextureCache ensuring the resource is destroyed
78   // on the render thread. (And avoiding a potentially problematic virtual call in the destructor)
79 }
80
81 bool BitmapTexture::HasAlphaChannel() const
82 {
83   return Pixel::HasAlpha(mPixelFormat);
84 }
85
86 bool BitmapTexture::IsFullyOpaque() const
87 {
88   if( mBitmap )
89   {
90     return mBitmap->IsFullyOpaque();
91   }
92   else
93   {
94     return ! HasAlphaChannel(); // Todo: amalgamate updated bitmap's IsFullyOpaque()
95   }
96 }
97
98 // Bitmap buffer has been changed. Upload changes to GPU.
99 void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char* pixels )
100 {
101   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
102   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
103
104   GLenum pixelFormat   = GL_RGBA;
105   GLenum pixelDataType = GL_UNSIGNED_BYTE;
106   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
107
108   mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
109
110   mContext.Bind2dTexture(mId);
111
112   if( ! updateArea.IsEmpty() )
113   {
114     mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
115     DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%d y:%d w:%d h:%d\n",
116                    updateArea.x, updateArea.y, updateArea.width ,updateArea.height );
117
118     const unsigned int pitchPixels = mWidth;
119     const unsigned int pixelDepth = Dali::Pixel::GetBytesPerPixel( mPixelFormat );
120
121     // If the width of the source update area is the same as the pitch, then can
122     // copy the contents in a single contiguous TexSubImage call.
123     if(updateArea.x == 0 && updateArea.width == pitchPixels)
124     {
125       pixels += updateArea.y * pitchPixels * pixelDepth;
126       mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, updateArea.y,
127                               updateArea.width, updateArea.height,
128                               pixelFormat, pixelDataType, pixels );
129     }
130     else
131     {
132       // Otherwise the source buffer needs to be copied line at a time, as OpenGL ES
133       // does not support source strides. (no GL_UNPACK_ROW_LENGTH supported)
134       unsigned int yBottom = updateArea.y + updateArea.height;
135       pixels += (updateArea.y * pitchPixels + updateArea.x) * pixelDepth;
136
137       for(unsigned int y = updateArea.y; y < yBottom; y++)
138       {
139         mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, y,
140                                 updateArea.width, 1,
141                                 pixelFormat, pixelDataType, pixels );
142         pixels += pitchPixels * pixelDepth;
143       }
144     }
145
146     INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED,
147                  updateArea.Area()* GetBytesPerPixel( mPixelFormat ));
148   }
149 }
150
151 void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pixels )
152 {
153   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
154   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AssignBitmap()\n");
155
156   GLenum pixelFormat = GL_RGBA;
157   GLenum pixelDataType = GL_UNSIGNED_BYTE;
158
159   if( generateTexture )
160   {
161     mContext.GenTextures(1, &mId);
162   }
163   DALI_ASSERT_DEBUG( mId != 0 );
164
165   mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
166   mContext.Bind2dTexture(mId);
167   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
168
169   mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
170   mContext.TexImage2D(GL_TEXTURE_2D, 0, pixelFormat, mWidth, mHeight, 0, pixelFormat, pixelDataType, pixels);
171   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
172   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
173
174   // If the resource policy is to discard on upload then release buffer
175   DiscardBitmapBuffer();
176
177   if( pixels != NULL )
178   {
179     INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, GetBytesPerPixel(mPixelFormat) * mWidth * mHeight );
180   }
181 }
182
183 void BitmapTexture::Update( Integration::Bitmap* bitmap )
184 {
185   DALI_LOG_INFO( Debug::Filter::gGLResource, Debug::General, "BitmapTexture::Update(bitmap:%p )\n", bitmap );
186   DALI_ASSERT_DEBUG( bitmap != 0 );
187   if( !bitmap )
188   {
189     DALI_LOG_ERROR( "Passed a null bitmap to update this bitmap texture." );
190     return;
191   }
192
193   // Only Packed-pixel bitmaps are ever associated with BitmapTextures, so we should never be passed any other kind:
194   const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixels = bitmap->GetPackedPixelsProfile();
195   DALI_ASSERT_DEBUG(bitmapPackedPixels);
196   if( !bitmapPackedPixels )
197   {
198     ///! This should never happen.
199     DALI_LOG_ERROR("Passed an incompatible bitmap type to update this bitmap texture.");
200     return;
201   }
202   mBitmap = bitmap;
203
204   const unsigned char* pixels = mBitmap->GetBuffer();
205
206   // We should never have null pixel data here - resource manager has deliberately loaded/reloaded data
207
208   DALI_ASSERT_DEBUG( pixels != NULL );
209
210   if( NULL == pixels )
211   {
212     DALI_LOG_ERROR("BitmapTexture::Upload() - Bitmap has no pixel data.\n");
213   }
214   else if( mId != 0 )
215   {
216     if( mImageWidth == mBitmap->GetImageWidth() &&
217         mImageHeight == mBitmap->GetImageHeight() &&
218         mWidth  == bitmapPackedPixels->GetBufferWidth() &&
219         mHeight == bitmapPackedPixels->GetBufferHeight() &&
220         mPixelFormat == mBitmap->GetPixelFormat() ) // and size hasn't changed
221     {
222       RectArea area(0, 0, mImageWidth, mImageHeight);  // just update whole texture
223       AreaUpdated( area, pixels );
224       DiscardBitmapBuffer();
225     }
226     else // Otherwise, reload the pixel data
227     {
228       mImageWidth  = mBitmap->GetImageWidth();
229       mImageHeight = mBitmap->GetImageHeight();
230       mWidth       = bitmapPackedPixels->GetBufferWidth();
231       mHeight      = bitmapPackedPixels->GetBufferHeight();
232       mPixelFormat = mBitmap->GetPixelFormat();
233
234       AssignBitmap( false, pixels );
235     }
236   }
237 }
238
239 void BitmapTexture::Update( Integration::Bitmap* srcBitmap, std::size_t xOffset, std::size_t yOffset )
240 {
241   if( NULL != srcBitmap )
242   {
243     Update( srcBitmap->GetBuffer(), srcBitmap->GetImageWidth(), srcBitmap->GetImageHeight(),
244             srcBitmap->GetPixelFormat(), xOffset, yOffset);
245   }
246 }
247
248 void BitmapTexture::Update( PixelData* srcPixelData, std::size_t xOffset, std::size_t yOffset )
249 {
250   if( NULL != srcPixelData )
251   {
252     Update( srcPixelData->GetBuffer(), srcPixelData->GetWidth(), srcPixelData->GetHeight(),
253             srcPixelData->GetPixelFormat(), xOffset, yOffset);
254   }
255 }
256
257 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 )
258 {
259
260   GLenum pixelGLFormat   = GL_RGBA;
261   GLenum pixelDataType = GL_UNSIGNED_BYTE;
262   Integration::ConvertToGlFormat( mPixelFormat, pixelDataType, pixelGLFormat );
263
264   if( !mId )
265   {
266     mContext.GenTextures( 1, &mId );
267
268     mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
269     mContext.Bind2dTexture( mId );
270     mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
271
272     mContext.TexImage2D( GL_TEXTURE_2D, 0, pixelGLFormat, mWidth, mHeight, 0, pixelGLFormat, pixelDataType, NULL );
273     mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
274     mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
275   }
276   else
277   {
278     mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
279     mContext.Bind2dTexture( mId );
280     mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
281   }
282
283 #if DALI_GLES_VERSION >= 30
284 // for gles 3.0, uploading sub-image with different format is a valid operation
285   Integration::ConvertToGlFormat( srcBitmap->GetPixelFormat(), pixelDataType, pixelGLFormat );
286 #else
287   // allows RGB888 source bitmap to be added to RGBA8888 texture, need to convert the bitmap format manually
288   if(pixelFormat == Pixel::RGB888 && mPixelFormat == Pixel::RGBA8888 )
289   {
290     std::size_t size = width * height;
291
292     Vector<PixelBuffer> tempBuffer;
293     tempBuffer.Reserve( size*4u );
294     PixelBuffer* data = tempBuffer.Begin();
295
296     for( std::size_t i=0u; i<size; i++ )
297     {
298       data[i*4u] = pixels[i*3u];
299       data[i*4u+1] = pixels[i*3u+1];
300       data[i*4u+2] = pixels[i*3u+2];
301       data[i*4u+3] = 0xFF;
302     }
303
304     mContext.TexSubImage2D( GL_TEXTURE_2D, 0,
305                             xOffset, yOffset,
306                             width, height,
307                             pixelGLFormat, pixelDataType,
308                             data );
309
310     return;
311   }
312 #endif
313
314   mContext.TexSubImage2D( GL_TEXTURE_2D, 0,
315                           xOffset, yOffset,
316                           width, height,
317                           pixelGLFormat, pixelDataType,
318                           pixels );
319 }
320
321 void BitmapTexture::UpdateArea( const RectArea& updateArea )
322 {
323   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "BitmapTexture::UpdateArea()\n");
324
325   if( mBitmap != 0 )
326   {
327     const unsigned char* pixels = mBitmap->GetBuffer();
328
329     // Pixel data could be null if we've uploaded to GL and discarded the data.
330
331     if( NULL != pixels )
332     {
333       if( mId ) // If the texture is already bound
334       {
335         if( updateArea.IsEmpty() )
336         {
337           RectArea area;
338           area.x = 0;
339           area.y = 0;
340           area.width = mImageWidth;
341           area.height = mImageHeight;
342           AreaUpdated( area, pixels );
343         }
344         else
345         {
346           AreaUpdated( updateArea, pixels );
347         }
348       }
349     }
350   }
351 }
352
353 bool BitmapTexture::UpdateOnCreate()
354 {
355   return true;
356 }
357
358 bool BitmapTexture::CreateGlTexture()
359 {
360   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
361   DALI_LOG_INFO(Debug::Filter::gImage, Debug::Verbose, "BitmapTexture::CreateGlTexture() Bitmap: %s\n", DALI_LOG_GET_OBJECT_C_STR(this));
362
363   if( mBitmap )
364   {
365     const unsigned char* pixels = mBitmap->GetBuffer();
366
367     // pixel data could be NULL here if we've had a context loss and we previously discarded
368     // the pixel data on the previous upload. If it is null, then we shouldn't generate a
369     // new GL Texture; leaving mId as zero. Eventually, the bitmap will get reloaded,
370     // and pixels will become non-null.
371
372     if( NULL != pixels )
373     {
374       AssignBitmap( true, pixels );
375       DiscardBitmapBuffer();
376     }
377   }
378   else
379   {
380     const unsigned char* pixels = NULL;
381     Dali::Vector<unsigned char> pixelData; // Okay to create outside branch as empty vector has no heap allocation.
382     if( true == mClearPixels )
383     {
384       unsigned int size = mWidth * mHeight * Pixel::GetBytesPerPixel( mPixelFormat );
385       pixelData.Resize( size, 0 );
386       pixels = &pixelData[0];
387     }
388     AssignBitmap( true, pixels );
389   }
390
391   return mId != 0;
392 }
393
394 bool BitmapTexture::Init()
395 {
396   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
397   // mBitmap should be initialized by now
398   return (mBitmap != 0);
399 }
400
401 unsigned int BitmapTexture::GetWidth() const
402 {
403   unsigned int width = mWidth;
404   if( mBitmap )
405   {
406     width = mBitmap->GetImageWidth();
407   }
408   return width;
409 }
410
411 unsigned int BitmapTexture::GetHeight() const
412 {
413   unsigned int height = mHeight;
414   if( mBitmap )
415   {
416     height = mBitmap->GetImageHeight();
417   }
418   return height;
419 }
420
421 void BitmapTexture::DiscardBitmapBuffer()
422 {
423   DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "BitmapTexture::DiscardBitmapBuffer() DiscardPolicy: %s\n", mDiscardPolicy == ResourcePolicy::OWNED_DISCARD?"DISCARD":"RETAIN");
424
425   if( ResourcePolicy::OWNED_DISCARD == mDiscardPolicy )
426   {
427     DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "  Discarding bitmap\n");
428     if ( mBitmap )
429     {
430       mBitmap->DiscardBuffer();
431     }
432   }
433 }
434
435
436 } //namespace Internal
437
438 } //namespace Dali