Merge "Size negotiation patch 3: Scope size negotiation enums" into 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 Dali::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 Dali::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 = Internal::ThreadLocalStorage::Get().GetPlatformAbstraction();
156
157   Vector2 closestSize;
158   platformAbstraction.GetClosestImageSize( filename, attributes, closestSize );
159   ImageAttributes loadedAttrs;
160   loadedAttrs.SetSize( closestSize );
161   mWidth = closestSize.width;
162   mHeight = closestSize.height;
163
164   Integration::BitmapResourceType resourceType( loadedAttrs );
165
166   // Note, bitmap is only destroyed when the image is destroyed.
167   Integration::ResourcePointer resource = platformAbstraction.LoadResourceSynchronously(resourceType, filename);
168   if( resource )
169   {
170     mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
171   }
172   else
173   {
174     mBitmap.Reset();
175   }
176 }
177
178 NinePatchImage* NinePatchImage::DownCast( Image* image)
179 {
180   return dynamic_cast<NinePatchImage*>(image);
181 }
182
183 NinePatchImage::~NinePatchImage()
184 {
185 }
186
187 Vector4 NinePatchImage::GetStretchBorders()
188 {
189   if( ! mParsedBorder )
190   {
191     ParseBorders();
192   }
193   return mStretchBorders;
194 }
195
196 Rect<int> NinePatchImage::GetChildRectangle()
197 {
198   if( ! mParsedBorder )
199   {
200     ParseBorders();
201   }
202   return mChildRectangle;
203 }
204
205 Internal::BufferImagePtr NinePatchImage::CreateCroppedBufferImage()
206 {
207   BufferImagePtr cropped;
208
209   if( ! mBitmap )
210   {
211     DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
212   }
213   else
214   {
215     Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
216
217     cropped = BufferImage::New( mWidth-2, mHeight-2, pixelFormat, Dali::Image::NEVER );
218
219     Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
220     DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
221
222     if( srcProfile )
223     {
224       PixelBuffer* destPixels = cropped->GetBuffer();
225       unsigned int destStride = cropped->GetBufferStride();
226       unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
227
228       PixelBuffer* srcPixels = mBitmap->GetBuffer();
229       unsigned int srcStride = srcProfile->GetBufferStride();
230
231       for( unsigned int row=1; row < mHeight-1; ++row )
232       {
233         PixelBuffer* src  = srcPixels + row*srcStride + pixelWidth;
234         PixelBuffer* dest = destPixels + (row-1)*destStride;
235         memcpy(dest, src, destStride );
236       }
237     }
238
239     RectArea area;
240     cropped->Update(area); // default area has no width or height
241   }
242   return cropped;
243 }
244
245 void NinePatchImage::Connect()
246 {
247   if( !mTicket )
248   {
249     if( mBitmap )
250     {
251       const ImageTicketPtr& t = mResourceClient->AddBitmapImage(mBitmap.Get());
252       mTicket = t.Get();
253       mTicket->AddObserver(*this);
254     }
255   }
256
257   ++mConnectionCount;
258 }
259
260 void NinePatchImage::Disconnect()
261 {
262   if( mConnectionCount > 0 )
263   {
264     --mConnectionCount;
265   }
266 }
267
268
269 void NinePatchImage::ParseBorders()
270 {
271   if( ! mBitmap )
272   {
273     DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
274     return;
275   }
276
277   Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
278
279   Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
280   DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
281
282   if( srcProfile )
283   {
284     unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
285     PixelBuffer* srcPixels = mBitmap->GetBuffer();
286     unsigned int srcStride = srcProfile->GetBufferStride();
287
288     int alphaByte=0;
289     int alphaBits=0;
290     Pixel::GetAlphaOffsetAndMask(pixelFormat, alphaByte, alphaBits);
291     int redByte=0;
292     int redBits=0;
293     GetRedOffsetAndMask(pixelFormat, redByte, redBits);
294
295     int testByte = alphaByte;
296     int testBits = alphaBits;
297     int testValue = alphaBits; // Opaque == stretch
298     if( ! alphaBits )
299     {
300       testByte = redByte;
301       testBits = redBits;
302       testValue = 0;           // Black == stretch
303     }
304
305     int startX1=-1;
306     int endX1=-1;
307     int startY1=-1;
308     int endY1=-1;
309     int startX2=-1;
310     int endX2=-1;
311     int startY2=-1;
312     int endY2=-1;
313
314     PixelBuffer* top = srcPixels + pixelWidth;
315     PixelBuffer* bottom = srcPixels + (mHeight-1)*srcStride + pixelWidth;
316
317     // Read the top and bottom rows:
318     // (Also read the last column to ensure end value gets set)
319     for( unsigned int col=1; col < mWidth; ++col )
320     {
321       if( (top[testByte] & testBits) == testValue )
322       {
323         if(startX1 < 0)
324         {
325           startX1 = col;
326         }
327       }
328       else if(startX1 >= 0 && endX1 < 0)
329       {
330         endX1 = col;
331       }
332
333       if( (bottom[testByte] & testBits) == testValue )
334       {
335         if(startX2 < 0)
336         {
337           startX2 = col;
338         }
339       }
340       else if(startX2 >= 0 && endX2 < 0)
341       {
342         endX2 = col;
343       }
344
345       if ( ( endX2 > 0 ) && ( endX1 > 0 ) )
346       {
347         break;
348       }
349
350       top+=pixelWidth;
351       bottom+=pixelWidth;
352     }
353
354     // Read the left and right columns:
355     PixelBuffer* left  = srcPixels + srcStride;
356     PixelBuffer* right = left + (srcStride - pixelWidth);
357
358     // (Also read the last row to ensure end value gets set)
359     for( unsigned int row=1; row < mHeight; ++row )
360     {
361       if((left[testByte] & testBits) == testValue)
362       {
363         if(startY1 < 0)
364         {
365           startY1 = row;
366         }
367       }
368       else if(startY1 >= 0 && endY1 < 0)
369       {
370         endY1 = row;
371       }
372
373       if((right[testByte] & testBits) == testValue)
374       {
375         if(startY2 < 0)
376         {
377           startY2 = row;
378         }
379       }
380       else if(startY2 >= 0 && endY2 < 0)
381       {
382         endY2 = row;
383       }
384       left += srcStride;
385       right += srcStride;
386
387       if ( ( endY2 > 0 ) && ( endY1 > 0 ) )
388       {
389         break;
390       }
391     }
392
393     mStretchBorders.x = startX1;
394     mStretchBorders.y = startY1;
395     mStretchBorders.z = mWidth-endX1;
396     mStretchBorders.w = mHeight-endY1;
397
398     mChildRectangle.x = startX2;
399     mChildRectangle.y = startY2;
400     mChildRectangle.width = endX2-startX2;
401     mChildRectangle.height = endY2-startY2;
402
403     mParsedBorder = true;
404   }
405 }
406
407 } // namespace Internal
408
409 } // namespace Dali