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