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