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