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