BufferImage mirrors an external Pixel Buffer on creation.
[platform/core/uifw/dali-core.git] / dali / internal / event / images / nine-patch-image-impl.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/event/images/nine-patch-image-impl.h>
20
21 // EXTERNAL INCLUDES
22 #include <cstring> // for memcmp
23
24 // INTERNAL INCLUDES
25 #include <dali/public-api/object/type-registry.h>
26 #include <dali/integration-api/bitmap.h>
27 #include <dali/internal/event/common/thread-local-storage.h>
28 #include <dali/internal/event/resources/resource-client.h>
29 #include <dali/internal/update/manager/update-manager.h>
30 #include <dali/internal/event/images/image-factory.h>
31 #include <dali/integration-api/platform-abstraction.h>
32 #include <dali/integration-api/resource-types.h>
33 #include <dali/integration-api/resource-cache.h>
34
35
36 namespace
37 {
38 void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask)
39 {
40   switch (pixelFormat)
41   {
42     case Dali::Pixel::A8:
43     case Dali::Pixel::L8:
44     case Dali::Pixel::LA88:
45     {
46       byteOffset=0;
47       bitMask=0;
48       break;
49     }
50
51     case Dali::Pixel::RGB888:
52     case Dali::Pixel::RGB8888:
53     case Dali::Pixel::RGBA8888:
54     {
55       byteOffset=0;
56       bitMask=0xFF;
57       break;
58     }
59     case Dali::Pixel::BGR8888:
60     case Dali::Pixel::BGRA8888:
61     {
62       byteOffset=2;
63       bitMask=0xff;
64       break;
65     }
66     case Dali::Pixel::RGB565:
67     {
68       byteOffset=0;
69       bitMask=0xf8;
70       break;
71     }
72     case Dali::Pixel::BGR565:
73     {
74       byteOffset=1;
75       bitMask=0x1f;
76       break;
77     }
78
79     case Dali::Pixel::RGBA4444:
80     {
81       byteOffset=0;
82       bitMask=0xf0;
83       break;
84     }
85     case Dali::Pixel::BGRA4444:
86     {
87       byteOffset=1;
88       bitMask=0xf0;
89       break;
90     }
91
92     case Dali::Pixel::RGBA5551:
93     {
94       byteOffset=0;
95       bitMask=0xf8;
96       break;
97     }
98
99     case Dali::Pixel::BGRA5551:
100     {
101       byteOffset=1;
102       bitMask=0x1e;
103       break;
104     }
105
106     case Dali::Pixel::COMPRESSED_R11_EAC:
107     case Dali::Pixel::COMPRESSED_SIGNED_R11_EAC:
108     case Dali::Pixel::COMPRESSED_RG11_EAC:
109     case Dali::Pixel::COMPRESSED_SIGNED_RG11_EAC:
110     case Dali::Pixel::COMPRESSED_RGB8_ETC2:
111     case Dali::Pixel::COMPRESSED_SRGB8_ETC2:
112     case Dali::Pixel::COMPRESSED_RGB8_ETC1:
113     case Dali::Pixel::COMPRESSED_RGB_PVRTC_4BPPV1:
114     case Dali::Pixel::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
115     case Dali::Pixel::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
116     case Dali::Pixel::COMPRESSED_RGBA8_ETC2_EAC:
117     case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
118     {
119       DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
120       byteOffset=0;
121       bitMask=0;
122       break;
123     }
124   }
125 }
126
127 } // anonymous namespace
128
129
130 namespace Dali
131 {
132 namespace Internal
133 {
134
135 namespace
136 {
137 TypeRegistration mType( typeid( Dali::NinePatchImage ), typeid( Dali::Image ), NULL );
138 } // unnamed namespace
139
140 NinePatchImagePtr NinePatchImage::New( const std::string& filename, const ImageAttributes& attributes, ReleasePolicy releasePol )
141 {
142   Internal::NinePatchImagePtr internal( new NinePatchImage( filename, attributes, releasePol ) );
143   internal->Initialize();
144   return internal;
145 }
146
147 NinePatchImage::NinePatchImage( const std::string& filename, const ImageAttributes& attributes, ReleasePolicy releasePol )
148 : ResourceImage( IMAGE_LOAD_POLICY_DEFAULT, releasePol ),
149   mParsedBorder(false)
150 {
151   ThreadLocalStorage& tls = ThreadLocalStorage::Get();
152   mResourceClient = &tls.GetResourceClient();
153
154   Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
155   Integration::BitmapResourceType resourceType( ImageDimensions::FromFloatVec2( attributes.GetSize() ), attributes.GetScalingMode(), attributes.GetFilterMode(), attributes.GetOrientationCorrection() );
156
157   // Note, bitmap is only destroyed when the image is destroyed.
158   Integration::ResourcePointer resource = platformAbstraction.LoadResourceSynchronously( resourceType, filename );
159   if( resource )
160   {
161     mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
162     mWidth = mBitmap->GetImageWidth();
163     mHeight = mBitmap->GetImageHeight();
164   }
165   else
166   {
167     mBitmap.Reset();
168     mWidth = 0;
169     mHeight = 0;
170   }
171 }
172
173 NinePatchImage* NinePatchImage::DownCast( Image* image)
174 {
175   return dynamic_cast<NinePatchImage*>(image);
176 }
177
178 NinePatchImage::~NinePatchImage()
179 {
180 }
181
182 Vector4 NinePatchImage::GetStretchBorders()
183 {
184   if( ! mParsedBorder )
185   {
186     ParseBorders();
187   }
188   return mStretchBorders;
189 }
190
191 Rect<int> NinePatchImage::GetChildRectangle()
192 {
193   if( ! mParsedBorder )
194   {
195     ParseBorders();
196   }
197   return mChildRectangle;
198 }
199
200 Internal::BufferImagePtr NinePatchImage::CreateCroppedBufferImage()
201 {
202   BufferImagePtr cropped;
203
204   if( ! mBitmap )
205   {
206     DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
207   }
208   else
209   {
210     Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
211
212     cropped = BufferImage::New( mWidth-2, mHeight-2, pixelFormat, Dali::Image::NEVER );
213
214     Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
215     DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
216
217     if( srcProfile )
218     {
219       PixelBuffer* destPixels = cropped->GetBuffer();
220       unsigned int destStride = cropped->GetBufferStride();
221       unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
222
223       PixelBuffer* srcPixels = mBitmap->GetBuffer();
224       unsigned int srcStride = srcProfile->GetBufferStride();
225
226       for( unsigned int row=1; row < mHeight-1; ++row )
227       {
228         PixelBuffer* src  = srcPixels + row*srcStride + pixelWidth;
229         PixelBuffer* dest = destPixels + (row-1)*destStride;
230         memcpy(dest, src, destStride );
231       }
232     }
233
234     RectArea area;
235     cropped->Update(area); // default area has no width or height
236   }
237   return cropped;
238 }
239
240 void NinePatchImage::Connect()
241 {
242   if( !mTicket )
243   {
244     if( mBitmap )
245     {
246       const ImageTicketPtr& t = mResourceClient->AddBitmapImage(mBitmap.Get());
247       mTicket = t.Get();
248       mTicket->AddObserver(*this);
249     }
250   }
251
252   ++mConnectionCount;
253 }
254
255 void NinePatchImage::Disconnect()
256 {
257   if( mConnectionCount > 0 )
258   {
259     --mConnectionCount;
260   }
261 }
262
263
264 void NinePatchImage::ParseBorders()
265 {
266   if( ! mBitmap )
267   {
268     DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
269     return;
270   }
271
272   Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
273
274   Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
275   DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
276
277   if( srcProfile )
278   {
279     unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
280     PixelBuffer* srcPixels = mBitmap->GetBuffer();
281     unsigned int srcStride = srcProfile->GetBufferStride();
282
283     int alphaByte=0;
284     int alphaBits=0;
285     Pixel::GetAlphaOffsetAndMask(pixelFormat, alphaByte, alphaBits);
286     int redByte=0;
287     int redBits=0;
288     GetRedOffsetAndMask(pixelFormat, redByte, redBits);
289
290     int testByte = alphaByte;
291     int testBits = alphaBits;
292     int testValue = alphaBits; // Opaque == stretch
293     if( ! alphaBits )
294     {
295       testByte = redByte;
296       testBits = redBits;
297       testValue = 0;           // Black == stretch
298     }
299
300     int startX1=-1;
301     int endX1=-1;
302     int startY1=-1;
303     int endY1=-1;
304     int startX2=-1;
305     int endX2=-1;
306     int startY2=-1;
307     int endY2=-1;
308
309     PixelBuffer* top = srcPixels + pixelWidth;
310     PixelBuffer* bottom = srcPixels + (mHeight-1)*srcStride + pixelWidth;
311
312     // Read the top and bottom rows:
313     // (Also read the last column to ensure end value gets set)
314     for( unsigned int col=1; col < mWidth; ++col )
315     {
316       if( (top[testByte] & testBits) == testValue )
317       {
318         if(startX1 < 0)
319         {
320           startX1 = col;
321         }
322       }
323       else if(startX1 >= 0 && endX1 < 0)
324       {
325         endX1 = col;
326       }
327
328       if( (bottom[testByte] & testBits) == testValue )
329       {
330         if(startX2 < 0)
331         {
332           startX2 = col;
333         }
334       }
335       else if(startX2 >= 0 && endX2 < 0)
336       {
337         endX2 = col;
338       }
339
340       if ( ( endX2 > 0 ) && ( endX1 > 0 ) )
341       {
342         break;
343       }
344
345       top+=pixelWidth;
346       bottom+=pixelWidth;
347     }
348
349     // Read the left and right columns:
350     PixelBuffer* left  = srcPixels + srcStride;
351     PixelBuffer* right = left + (srcStride - pixelWidth);
352
353     // (Also read the last row to ensure end value gets set)
354     for( unsigned int row=1; row < mHeight; ++row )
355     {
356       if((left[testByte] & testBits) == testValue)
357       {
358         if(startY1 < 0)
359         {
360           startY1 = row;
361         }
362       }
363       else if(startY1 >= 0 && endY1 < 0)
364       {
365         endY1 = row;
366       }
367
368       if((right[testByte] & testBits) == testValue)
369       {
370         if(startY2 < 0)
371         {
372           startY2 = row;
373         }
374       }
375       else if(startY2 >= 0 && endY2 < 0)
376       {
377         endY2 = row;
378       }
379       left += srcStride;
380       right += srcStride;
381
382       if ( ( endY2 > 0 ) && ( endY1 > 0 ) )
383       {
384         break;
385       }
386     }
387
388     mStretchBorders.x = startX1;
389     mStretchBorders.y = startY1;
390     mStretchBorders.z = mWidth-endX1;
391     mStretchBorders.w = mHeight-endY1;
392
393     mChildRectangle.x = startX2;
394     mChildRectangle.y = startY2;
395     mChildRectangle.width = endX2-startX2;
396     mChildRectangle.height = endY2-startY2;
397
398     mParsedBorder = true;
399   }
400 }
401
402 } // namespace Internal
403
404 } // namespace Dali