Minimal Atlas API
[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            bitmap->GetPixelFormat()),
45   mBitmap(bitmap),
46   mClearPixels(false),
47   mDiscardPolicy(policy)
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            pixelFormat),
64   mBitmap(NULL),
65   mClearPixels(clearPixels),
66   mDiscardPolicy(policy)
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 void BitmapTexture::UploadBitmapArray( const BitmapUploadArray& bitmapArray )
80 {
81   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
82
83   if( mId == 0 )
84   {
85     CreateGlTexture();
86   }
87
88   mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
89   mContext.Bind2dTexture(mId);
90   mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
91
92   GLenum pixelFormat   = GL_RGBA;
93   GLenum pixelDataType = GL_UNSIGNED_BYTE;
94
95   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
96
97   // go through each bitmap uploading it
98
99   for( BitmapUploadArray::const_iterator iter =  bitmapArray.begin(), endIter = bitmapArray.end(); iter != endIter ; ++iter)
100   {
101     const BitmapUpload& bitmapItem((*iter));
102
103     DALI_ASSERT_ALWAYS(bitmapItem.mPixelData);
104
105     const unsigned char* pixels = bitmapItem.mPixelData;
106
107     DALI_ASSERT_DEBUG( (pixels!=NULL) && "bitmap has no data \n");
108
109     unsigned int bitmapWidth(bitmapItem.mWidth);
110     unsigned int bitmapHeight(bitmapItem.mHeight);
111
112     DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "upload bitmap to texture :%d y:%d w:%d h:%d\n",
113                             bitmapItem.mXpos,
114                             bitmapItem.mYpos,
115                             bitmapWidth,
116                             bitmapHeight);
117
118     mContext.TexSubImage2D(GL_TEXTURE_2D,
119                            0,                   /* mip map level */
120                            bitmapItem.mXpos,    /* X pos */
121                            bitmapItem.mYpos,    /* Y pos */
122                            bitmapWidth,         /* width */
123                            bitmapHeight,        /* height */
124                            pixelFormat,         /* our bitmap format (should match internal format) */
125                            pixelDataType,       /* pixel data type */
126                            pixels);             /* texture data */
127
128     if( BitmapUpload::DISCARD_PIXEL_DATA == bitmapItem.mDiscard)
129     {
130       delete [] bitmapItem.mPixelData;
131     }
132   }
133 }
134
135 bool BitmapTexture::HasAlphaChannel() const
136 {
137   return Pixel::HasAlpha(mPixelFormat);
138 }
139
140 bool BitmapTexture::IsFullyOpaque() const
141 {
142   if( mBitmap )
143   {
144     return mBitmap->IsFullyOpaque();
145   }
146   else
147   {
148     return ! HasAlphaChannel(); // Todo: amalgamate updated bitmap's IsFullyOpaque()
149   }
150 }
151
152 // Bitmap buffer has been changed. Upload changes to GPU.
153 void BitmapTexture::AreaUpdated( const RectArea& updateArea, const unsigned char* pixels )
154 {
155   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
156   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
157
158   GLenum pixelFormat   = GL_RGBA;
159   GLenum pixelDataType = GL_UNSIGNED_BYTE;
160   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
161
162   mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
163
164   mContext.Bind2dTexture(mId);
165
166   if( ! updateArea.IsEmpty() )
167   {
168     mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
169     DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%d y:%d w:%d h:%d\n",
170                    updateArea.x, updateArea.y, updateArea.width ,updateArea.height );
171
172     // TODO: obtain pitch of source image, obtain pixel depth of source image.
173     const unsigned int pitchPixels = mImageWidth;
174     const unsigned int pixelDepth = sizeof(unsigned int);
175
176     // If the width of the source update area is the same as the pitch, then can
177     // copy the contents in a single contiguous TexSubImage call.
178     if(updateArea.x == 0 && updateArea.width == pitchPixels)
179     {
180       pixels += updateArea.y * pitchPixels * pixelDepth;
181       mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, updateArea.y,
182                               updateArea.width, updateArea.height,
183                               pixelFormat, pixelDataType, pixels );
184     }
185     else
186     {
187       // Otherwise the source buffer needs to be copied line at a time, as OpenGL ES
188       // does not support source strides. (no GL_UNPACK_ROW_LENGTH supported)
189       unsigned int yBottom = updateArea.y + updateArea.height;
190       pixels += (updateArea.y * pitchPixels + updateArea.x) * pixelDepth;
191
192       for(unsigned int y = updateArea.y; y < yBottom; y++)
193       {
194         mContext.TexSubImage2D( GL_TEXTURE_2D,0, updateArea.x, y,
195                                 updateArea.width, 1,
196                                 pixelFormat, pixelDataType, pixels );
197         pixels += pitchPixels * pixelDepth;
198       }
199     }
200
201     INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED,
202                  updateArea.Area()* GetBytesPerPixel( mPixelFormat ));
203   }
204 }
205
206 void BitmapTexture::AssignBitmap( bool generateTexture, const unsigned char* pixels )
207 {
208   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
209   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AssignBitmap()\n");
210
211   GLenum pixelFormat = GL_RGBA;
212   GLenum pixelDataType = GL_UNSIGNED_BYTE;
213
214   if( generateTexture )
215   {
216     mContext.GenTextures(1, &mId);
217   }
218   DALI_ASSERT_DEBUG( mId != 0 );
219
220   mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
221   mContext.Bind2dTexture(mId);
222   Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
223
224   mContext.PixelStorei(GL_UNPACK_ALIGNMENT, 1); // We always use tightly packed data
225   mContext.TexImage2D(GL_TEXTURE_2D, 0, pixelFormat, mWidth, mHeight, 0, pixelFormat, pixelDataType, pixels);
226   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
227   mContext.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
228
229   INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, GetBytesPerPixel(mPixelFormat) * mWidth * mHeight );
230 }
231
232 void BitmapTexture::Update( Integration::Bitmap* bitmap )
233 {
234   DALI_LOG_INFO( Debug::Filter::gGLResource, Debug::General, "BitmapTexture::Update(bitmap:%p )\n", bitmap );
235   DALI_ASSERT_DEBUG( bitmap != 0 );
236   if( !bitmap )
237   {
238     DALI_LOG_ERROR( "Passed a null bitmap to update this bitmap texture." );
239     return;
240   }
241
242   // Only Packed-pixel bitmaps are ever associated with BitmapTextures, so we should never be passed any other kind:
243   const Integration::Bitmap::PackedPixelsProfile * const bitmapPackedPixels = bitmap->GetPackedPixelsProfile();
244   DALI_ASSERT_DEBUG(bitmapPackedPixels);
245   if( !bitmapPackedPixels )
246   {
247     ///! This should never happen.
248     DALI_LOG_ERROR("Passed an incompatible bitmap type to update this bitmap texture.");
249     return;
250   }
251   mBitmap = bitmap;
252
253   const unsigned char* pixels = mBitmap->GetBuffer();
254
255   // We should never have null pixel data here - resource manager has deliberately loaded/reloaded data
256
257   DALI_ASSERT_DEBUG( pixels != NULL );
258
259   if( NULL == pixels )
260   {
261     DALI_LOG_ERROR("BitmapTexture::Upload() - Bitmap has no pixel data.\n");
262   }
263   else if( mId != 0 )
264   {
265     if( mImageWidth == mBitmap->GetImageWidth() &&
266         mImageHeight == mBitmap->GetImageHeight() &&
267         mWidth  == bitmapPackedPixels->GetBufferWidth() &&
268         mHeight == bitmapPackedPixels->GetBufferHeight() &&
269         mPixelFormat == mBitmap->GetPixelFormat() ) // and size hasn't changed
270     {
271       RectArea area(0, 0, mImageWidth, mImageHeight);  // just update whole texture
272       AreaUpdated( area, pixels );
273       DiscardBitmapBuffer();
274     }
275     else // Otherwise, reload the pixel data
276     {
277       mImageWidth  = mBitmap->GetImageWidth();
278       mImageHeight = mBitmap->GetImageHeight();
279       mWidth       = bitmapPackedPixels->GetBufferWidth();
280       mHeight      = bitmapPackedPixels->GetBufferHeight();
281       mPixelFormat = mBitmap->GetPixelFormat();
282
283       AssignBitmap( false, pixels );
284     }
285   }
286 }
287
288 void BitmapTexture::Update( Integration::Bitmap* srcBitmap, std::size_t xOffset, std::size_t yOffset )
289 {
290   if( NULL != srcBitmap )
291   {
292     GLenum pixelFormat   = GL_RGBA;
293     GLenum pixelDataType = GL_UNSIGNED_BYTE;
294     Integration::ConvertToGlFormat( mPixelFormat, pixelDataType, pixelFormat );
295
296     if( !mId )
297     {
298       mContext.GenTextures( 1, &mId );
299
300       mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
301       mContext.Bind2dTexture( mId );
302       mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
303
304       mContext.TexImage2D( GL_TEXTURE_2D, 0, pixelFormat, mWidth, mHeight, 0, pixelFormat, pixelDataType, NULL );
305       mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
306       mContext.TexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
307     }
308     else
309     {
310       mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
311       mContext.Bind2dTexture( mId );
312       mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 );
313     }
314
315     const unsigned char* pixels = srcBitmap->GetBuffer();
316     unsigned int srcWidth = srcBitmap->GetImageWidth();
317     const unsigned int pixelDepth = Pixel::GetBytesPerPixel( mPixelFormat );
318
319     unsigned int yBottom = yOffset + srcBitmap->GetImageHeight();
320
321     for( unsigned int y = yOffset; y < yBottom; y++ )
322     {
323       mContext.TexSubImage2D( GL_TEXTURE_2D, 0,
324                               xOffset, y,
325                               srcWidth, 1,
326                               pixelFormat, pixelDataType, pixels );
327
328       pixels += srcWidth * pixelDepth;
329     }
330   }
331 }
332
333 void BitmapTexture::UpdateArea( const RectArea& updateArea )
334 {
335   DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::General, "BitmapTexture::UpdateArea()\n");
336
337   if( mBitmap != 0 )
338   {
339     const unsigned char* pixels = mBitmap->GetBuffer();
340
341     // Pixel data could be null if we've uploaded to GL and discarded the data.
342
343     if( NULL != pixels )
344     {
345       if( mId ) // If the texture is already bound
346       {
347         if( updateArea.IsEmpty() )
348         {
349           RectArea area;
350           area.x = 0;
351           area.y = 0;
352           area.width = mImageWidth;
353           area.height = mImageHeight;
354           AreaUpdated( area, pixels );
355         }
356         else
357         {
358           AreaUpdated( updateArea, pixels );
359         }
360       }
361     }
362   }
363 }
364
365 void BitmapTexture::ClearAreas( const BitmapClearArray& areaArray, std::size_t blockSize, uint32_t color )
366 {
367   if(mId > 0)
368   {
369     DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
370     DALI_LOG_INFO(Debug::Filter::gGLResource, Debug::Verbose, "BitmapTexture::AreaUpdated()\n");
371
372     GLenum pixelFormat   = GL_RGBA;
373     GLenum pixelDataType = GL_UNSIGNED_BYTE;
374     Integration::ConvertToGlFormat(mPixelFormat, pixelDataType, pixelFormat);
375
376     mContext.ActiveTexture( TEXTURE_UNIT_UPLOAD );
377     mContext.Bind2dTexture(mId);
378
379     size_t numPixels = blockSize*blockSize;
380     size_t bytesPerPixel = Pixel::GetBytesPerPixel(mPixelFormat);
381     char* clearPixels = (char*)malloc(numPixels * bytesPerPixel);
382
383     for(size_t i=0; i<numPixels; i++)
384     {
385       memcpy(&clearPixels[i*bytesPerPixel], &color, bytesPerPixel);
386     }
387
388     for( BitmapClearArray::const_iterator iter =  areaArray.begin(), endIter = areaArray.end(); iter != endIter ; ++iter)
389     {
390       const Vector2& clearPos((*iter));
391
392       mContext.PixelStorei( GL_UNPACK_ALIGNMENT, 1 ); // We always use tightly packed data
393       DALI_LOG_INFO( Debug::Filter::gImage, Debug::General, "Update x:%0.2f y:%0.2f w:%d h:%d\n",
394                      clearPos.x, clearPos.y, blockSize, blockSize );
395
396       mContext.TexSubImage2D(GL_TEXTURE_2D,
397                              0,
398                              clearPos.x,
399                              clearPos.y,
400                              blockSize,
401                              blockSize,
402                              pixelFormat,         /* our bitmap format (should match internal format) */
403                              pixelDataType,       /* pixel data type */
404                              clearPixels);        /* texture data */
405
406       INCREASE_BY( PerformanceMonitor::TEXTURE_DATA_UPLOADED, numPixels * bytesPerPixel );
407     }
408     free(clearPixels);
409   }
410 }
411
412 bool BitmapTexture::UpdateOnCreate()
413 {
414   return true;
415 }
416
417 bool BitmapTexture::CreateGlTexture()
418 {
419   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
420   DALI_LOG_INFO(Debug::Filter::gImage, Debug::Verbose, "BitmapTexture::CreateGlTexture() Bitmap: %s\n", DALI_LOG_GET_OBJECT_C_STR(this));
421
422   if( mBitmap )
423   {
424     const unsigned char* pixels = mBitmap->GetBuffer();
425
426     // pixel data could be NULL here if we've had a context loss and we previously discarded
427     // the pixel data on the previous upload. If it is null, then we shouldn't generate a
428     // new GL Texture; leaving mId as zero. Eventually, the bitmap will get reloaded,
429     // and pixels will become non-null.
430
431     if( NULL != pixels )
432     {
433       AssignBitmap( true, pixels );
434       DiscardBitmapBuffer();
435     }
436   }
437   else
438   {
439     const unsigned char* pixels = NULL;
440     std::vector<unsigned char> pixelData;
441     if( ( NULL == pixels ) && ( true == mClearPixels ) )
442     {
443       unsigned int size = mWidth * mHeight * Pixel::GetBytesPerPixel(mPixelFormat);
444       pixelData.resize(size, 0);
445       pixels = &pixelData[0];
446     }
447     AssignBitmap( true, pixels );
448   }
449
450   return mId != 0;
451 }
452
453 bool BitmapTexture::Init()
454 {
455   DALI_LOG_TRACE_METHOD(Debug::Filter::gImage);
456   // mBitmap should be initialized by now
457   return (mBitmap != 0);
458 }
459
460 unsigned int BitmapTexture::GetWidth() const
461 {
462   unsigned int width = mWidth;
463   if( mBitmap )
464   {
465     width = mBitmap->GetImageWidth();
466   }
467   return width;
468 }
469
470 unsigned int BitmapTexture::GetHeight() const
471 {
472   unsigned int height = mHeight;
473   if( mBitmap )
474   {
475     height = mBitmap->GetImageHeight();
476   }
477   return height;
478 }
479
480 void BitmapTexture::DiscardBitmapBuffer()
481 {
482   DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "BitmapTexture::DiscardBitmapBuffer() DiscardPolicy: %s\n", mDiscardPolicy == ResourcePolicy::DISCARD?"DISCARD":"RETAIN");
483
484   if( ResourcePolicy::DISCARD == mDiscardPolicy )
485   {
486     DALI_LOG_INFO(Debug::Filter::gImage, Debug::General, "  Discarding bitmap\n");
487     mBitmap->DiscardBuffer();
488   }
489 }
490
491
492 } //namespace Internal
493
494 } //namespace Dali