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