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