2 * Copyright (c) 2014 Samsung Electronics Co., Ltd.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 #include <dali/internal/event/images/nine-patch-image-impl.h>
22 #include <cstring> // for memcmp
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>
38 void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask)
44 case Dali::Pixel::LA88:
51 case Dali::Pixel::RGB888:
52 case Dali::Pixel::RGB8888:
53 case Dali::Pixel::RGBA8888:
59 case Dali::Pixel::BGR8888:
60 case Dali::Pixel::BGRA8888:
66 case Dali::Pixel::RGB565:
72 case Dali::Pixel::BGR565:
79 case Dali::Pixel::RGBA4444:
85 case Dali::Pixel::BGRA4444:
92 case Dali::Pixel::RGBA5551:
99 case Dali::Pixel::BGRA5551:
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:
119 DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
127 } // anonymous namespace
137 TypeRegistration mType( typeid( Dali::NinePatchImage ), typeid( Dali::Image ), NULL );
138 } // unnamed namespace
140 NinePatchImagePtr NinePatchImage::New( const std::string& filename, ReleasePolicy releasePol )
142 Internal::NinePatchImagePtr internal( new NinePatchImage( filename, releasePol ) );
143 internal->Initialize();
147 NinePatchImage::NinePatchImage( const std::string& filename, ReleasePolicy releasePol )
148 : ResourceImage( IMAGE_LOAD_POLICY_DEFAULT, releasePol ),
151 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
152 mResourceClient = &tls.GetResourceClient();
154 Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
155 Integration::BitmapResourceType resourceType;
157 // Note, bitmap is only destroyed when the image is destroyed.
158 Integration::ResourcePointer resource = platformAbstraction.LoadResourceSynchronously( resourceType, filename );
161 mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
162 mWidth = mBitmap->GetImageWidth();
163 mHeight = mBitmap->GetImageHeight();
173 NinePatchImage* NinePatchImage::DownCast( Image* image)
175 return dynamic_cast<NinePatchImage*>(image);
178 NinePatchImage::~NinePatchImage()
182 const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsX()
184 if( ! mParsedBorder )
188 return mStretchPixelsX;
191 const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsY()
193 if( ! mParsedBorder )
197 return mStretchPixelsY;
200 Rect<int> NinePatchImage::GetChildRectangle()
202 if( ! mParsedBorder )
206 return mChildRectangle;
209 Internal::BufferImagePtr NinePatchImage::CreateCroppedBufferImage()
211 BufferImagePtr cropped;
215 DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
219 Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
221 cropped = BufferImage::New( mWidth-2, mHeight-2, pixelFormat, Dali::Image::NEVER );
223 Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
224 DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
228 PixelBuffer* destPixels = cropped->GetBuffer();
229 unsigned int destStride = cropped->GetBufferStride();
230 unsigned int pixelWidth = GetBytesPerPixel(pixelFormat);
232 PixelBuffer* srcPixels = mBitmap->GetBuffer();
233 unsigned int srcStride = srcProfile->GetBufferStride();
235 for( unsigned int row=1; row < mHeight-1; ++row )
237 PixelBuffer* src = srcPixels + row*srcStride + pixelWidth;
238 PixelBuffer* dest = destPixels + (row-1)*destStride;
239 memcpy(dest, src, destStride );
244 cropped->Update(area); // default area has no width or height
249 void NinePatchImage::Connect()
255 const ImageTicketPtr& t = mResourceClient->AddBitmapImage(mBitmap.Get());
257 mTicket->AddObserver(*this);
264 void NinePatchImage::Disconnect()
266 if( mConnectionCount > 0 )
273 void NinePatchImage::ParseBorders()
277 DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
281 mStretchPixelsX.Clear();
282 mStretchPixelsY.Clear();
284 Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
286 const Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
287 DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap" );
293 Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits );
295 int testByte = alphaByte;
296 int testBits = alphaBits;
297 int testValue = alphaBits; // Opaque == stretch
300 GetRedOffsetAndMask( pixelFormat, testByte, testBits );
301 testValue = 0; // Black == stretch
304 unsigned int pixelWidth = GetBytesPerPixel( pixelFormat );
305 const PixelBuffer* srcPixels = mBitmap->GetBuffer();
306 unsigned int srcStride = srcProfile->GetBufferStride();
309 const PixelBuffer* top = srcPixels + pixelWidth;
310 unsigned int index = 0;
311 unsigned int width = mBitmap->GetImageWidth();
312 unsigned int height = mBitmap->GetImageHeight();
314 for(; index < width - 2; )
316 Uint16Pair range = ParseRange( index, width - 2, top, pixelWidth, testByte, testBits, testValue );
317 if( range.GetX() != 0xFFFF )
319 mStretchPixelsX.PushBack( range );
324 const PixelBuffer* left = srcPixels + srcStride;
326 for(; index < height - 2; )
328 Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue );
329 if( range.GetX() != 0xFFFF )
331 mStretchPixelsY.PushBack( range );
335 //If there are no stretch pixels then make the entire image stretchable
336 if( mStretchPixelsX.Size() == 0 )
338 mStretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) );
340 if( mStretchPixelsY.Size() == 0 )
342 mStretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) );
347 const PixelBuffer* bottom = srcPixels + ( height - 1 ) * srcStride + pixelWidth;
349 Uint16Pair contentRangeX = ParseRange( index, width - 2, bottom, pixelWidth, testByte, testBits, testValue );
350 if( contentRangeX.GetX() == 0xFFFF )
352 contentRangeX = Uint16Pair();
356 const PixelBuffer* right = srcPixels + srcStride + ( width - 1 ) * pixelWidth;
358 Uint16Pair contentRangeY = ParseRange( index, height - 2, right, srcStride, testByte, testBits, testValue );
359 if( contentRangeY.GetX() == 0xFFFF )
361 contentRangeY = Uint16Pair();
364 mChildRectangle.x = contentRangeX.GetX() + 1;
365 mChildRectangle.y = contentRangeY.GetX() + 1;
366 mChildRectangle.width = contentRangeX.GetY() - contentRangeX.GetX();
367 mChildRectangle.height = contentRangeY.GetY() - contentRangeY.GetX();
369 mParsedBorder = true;
373 Uint16Pair NinePatchImage::ParseRange( unsigned int& index, unsigned int width, const PixelBuffer* & pixel, unsigned int pixelStride, int testByte, int testBits, int testValue )
375 unsigned int start = 0xFFFF;
376 for( ; index < width; ++index, pixel += pixelStride )
378 if( ( pixel[ testByte ] & testBits ) == testValue )
382 pixel += pixelStride;
387 unsigned int end = width;
388 for( ; index < width; ++index, pixel += pixelStride )
390 if( ( pixel[ testByte ] & testBits ) != testValue )
394 pixel += pixelStride;
399 return Uint16Pair( start, end );
402 bool NinePatchImage::IsNinePatchUrl( const std::string& url )
406 std::string::const_reverse_iterator iter = url.rbegin();
407 enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
408 while(iter < url.rend())
418 else if(!isalnum(*iter))
426 if( *iter == '#' || *iter == '9' )
442 state = DONE; // Stop testing characters
462 } // namespace Internal