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