2 * Copyright (c) 2018 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/update/manager/update-manager.h>
29 #include <dali/integration-api/platform-abstraction.h>
30 #include <dali/integration-api/resource-types.h>
35 void GetRedOffsetAndMask(Dali::Pixel::Format pixelFormat, int& byteOffset, int& bitMask)
41 case Dali::Pixel::LA88:
48 case Dali::Pixel::RGB888:
49 case Dali::Pixel::RGB8888:
50 case Dali::Pixel::RGBA8888:
56 case Dali::Pixel::BGR8888:
57 case Dali::Pixel::BGRA8888:
63 case Dali::Pixel::RGB565:
69 case Dali::Pixel::BGR565:
76 case Dali::Pixel::RGBA4444:
82 case Dali::Pixel::BGRA4444:
89 case Dali::Pixel::RGBA5551:
96 case Dali::Pixel::BGRA5551:
103 case Dali::Pixel::INVALID:
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 case Dali::Pixel::COMPRESSED_RGBA_ASTC_4x4_KHR:
117 case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x4_KHR:
118 case Dali::Pixel::COMPRESSED_RGBA_ASTC_5x5_KHR:
119 case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x5_KHR:
120 case Dali::Pixel::COMPRESSED_RGBA_ASTC_6x6_KHR:
121 case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x5_KHR:
122 case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x6_KHR:
123 case Dali::Pixel::COMPRESSED_RGBA_ASTC_8x8_KHR:
124 case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x5_KHR:
125 case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x6_KHR:
126 case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x8_KHR:
127 case Dali::Pixel::COMPRESSED_RGBA_ASTC_10x10_KHR:
128 case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x10_KHR:
129 case Dali::Pixel::COMPRESSED_RGBA_ASTC_12x12_KHR:
130 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
131 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:
132 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:
133 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:
134 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:
135 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:
136 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:
137 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
138 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:
139 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:
140 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:
141 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:
142 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:
143 case Dali::Pixel::COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:
145 DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple masking-out of per-pixel alpha.\n");
151 case Dali::Pixel::RGB16F:
152 case Dali::Pixel::RGB32F:
153 case Dali::Pixel::DEPTH_UNSIGNED_INT:
154 case Dali::Pixel::DEPTH_FLOAT:
155 case Dali::Pixel::DEPTH_STENCIL:
157 DALI_LOG_ERROR("Pixel format not compatible.\n");
165 } // anonymous namespace
173 NinePatchImagePtr NinePatchImage::New( const std::string& filename )
175 Internal::NinePatchImagePtr internal( new NinePatchImage( filename ) );
176 internal->Initialize();
180 NinePatchImage::NinePatchImage( const std::string& filename )
185 ThreadLocalStorage& tls = ThreadLocalStorage::Get();
187 Integration::PlatformAbstraction& platformAbstraction = tls.GetPlatformAbstraction();
188 Integration::BitmapResourceType resourceType;
190 // Note, bitmap is only destroyed when the image is destroyed.
191 Integration::ResourcePointer resource = platformAbstraction.LoadImageSynchronously( resourceType, filename );
194 mBitmap = static_cast<Integration::Bitmap*>( resource.Get());
196 mWidth = mBitmap->GetImageWidth();
197 mHeight = mBitmap->GetImageHeight();
198 mTexture = Texture::New( Dali::TextureType::TEXTURE_2D, mBitmap->GetPixelFormat(), mWidth, mHeight );
200 uint32_t bufferSize = mBitmap->GetBufferSize();
201 uint8_t* buffer = new uint8_t[bufferSize];
202 memcpy( buffer, mBitmap->GetBuffer(), bufferSize );
203 PixelDataPtr pixelData = PixelData::New( buffer, bufferSize, mWidth, mHeight, mBitmap->GetPixelFormat(), Dali::PixelData::DELETE_ARRAY );
204 mTexture->Upload( pixelData );
214 NinePatchImage* NinePatchImage::DownCast( Image* image)
216 return dynamic_cast<NinePatchImage*>(image);
219 NinePatchImage::~NinePatchImage()
223 const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsX()
225 if( ! mParsedBorder )
229 return mStretchPixelsX;
232 const NinePatchImage::StretchRanges& NinePatchImage::GetStretchPixelsY()
234 if( ! mParsedBorder )
238 return mStretchPixelsY;
241 Rect<int> NinePatchImage::GetChildRectangle()
243 if( ! mParsedBorder )
247 return mChildRectangle;
250 Internal::BufferImagePtr NinePatchImage::CreateCroppedBufferImage()
252 BufferImagePtr cropped;
256 DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
260 Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
262 cropped = BufferImage::New( mWidth-2, mHeight-2, pixelFormat );
264 Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
265 DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap");
269 PixelBuffer* destPixels = cropped->GetBuffer();
270 uint32_t destStride = cropped->GetBufferStride();
271 uint32_t pixelWidth = GetBytesPerPixel(pixelFormat);
273 PixelBuffer* srcPixels = mBitmap->GetBuffer();
274 uint32_t srcStride = srcProfile->GetBufferStride();
276 for( uint32_t row=1; row < mHeight-1; ++row )
278 PixelBuffer* src = srcPixels + row*srcStride + pixelWidth;
279 PixelBuffer* dest = destPixels + (row-1)*destStride;
280 memcpy(dest, src, destStride );
285 cropped->Update(area); // default area has no width or height
290 const std::string& NinePatchImage::GetUrl() const
295 void NinePatchImage::ParseBorders()
299 DALI_LOG_ERROR( "NinePatchImage: Bitmap not loaded, cannot perform operation\n");
303 mStretchPixelsX.Clear();
304 mStretchPixelsY.Clear();
306 Pixel::Format pixelFormat = mBitmap->GetPixelFormat();
308 const Integration::Bitmap::PackedPixelsProfile* srcProfile = mBitmap->GetPackedPixelsProfile();
309 DALI_ASSERT_DEBUG( srcProfile && "Wrong profile for source bitmap" );
315 Pixel::GetAlphaOffsetAndMask( pixelFormat, alphaByte, alphaBits );
317 int testByte = alphaByte;
318 int testBits = alphaBits;
319 int testValue = alphaBits; // Opaque == stretch
322 GetRedOffsetAndMask( pixelFormat, testByte, testBits );
323 testValue = 0; // Black == stretch
326 uint32_t pixelWidth = GetBytesPerPixel( pixelFormat );
327 const PixelBuffer* srcPixels = mBitmap->GetBuffer();
328 uint32_t srcStride = srcProfile->GetBufferStride();
331 const PixelBuffer* top = srcPixels + pixelWidth;
333 uint32_t width = mBitmap->GetImageWidth();
334 uint32_t height = mBitmap->GetImageHeight();
336 for(; index < width - 2; )
338 Uint16Pair range = ParseRange( index, width - 2, top, pixelWidth, testByte, testBits, testValue );
339 if( range.GetX() != 0xFFFF )
341 mStretchPixelsX.PushBack( range );
346 const PixelBuffer* left = srcPixels + srcStride;
348 for(; index < height - 2; )
350 Uint16Pair range = ParseRange( index, height - 2, left, srcStride, testByte, testBits, testValue );
351 if( range.GetX() != 0xFFFF )
353 mStretchPixelsY.PushBack( range );
357 //If there are no stretch pixels then make the entire image stretchable
358 if( mStretchPixelsX.Size() == 0 )
360 mStretchPixelsX.PushBack( Uint16Pair( 0, width - 2 ) );
362 if( mStretchPixelsY.Size() == 0 )
364 mStretchPixelsY.PushBack( Uint16Pair( 0, height - 2 ) );
369 const PixelBuffer* bottom = srcPixels + ( height - 1 ) * srcStride + pixelWidth;
371 Uint16Pair contentRangeX = ParseRange( index, width - 2, bottom, pixelWidth, testByte, testBits, testValue );
372 if( contentRangeX.GetX() == 0xFFFF )
374 contentRangeX = Uint16Pair();
378 const PixelBuffer* right = srcPixels + srcStride + ( width - 1 ) * pixelWidth;
380 Uint16Pair contentRangeY = ParseRange( index, height - 2, right, srcStride, testByte, testBits, testValue );
381 if( contentRangeY.GetX() == 0xFFFF )
383 contentRangeY = Uint16Pair();
386 mChildRectangle.x = contentRangeX.GetX() + 1;
387 mChildRectangle.y = contentRangeY.GetX() + 1;
388 mChildRectangle.width = contentRangeX.GetY() - contentRangeX.GetX();
389 mChildRectangle.height = contentRangeY.GetY() - contentRangeY.GetX();
391 mParsedBorder = true;
395 Uint16Pair NinePatchImage::ParseRange( uint32_t& index, uint32_t width, const PixelBuffer* & pixel, uint32_t pixelStride, int testByte, int testBits, int testValue )
397 uint32_t start = 0xFFFF;
398 for( ; index < width; ++index, pixel += pixelStride )
400 if( ( pixel[ testByte ] & testBits ) == testValue )
404 pixel += pixelStride;
409 uint32_t end = width;
410 for( ; index < width; ++index, pixel += pixelStride )
412 if( ( pixel[ testByte ] & testBits ) != testValue )
416 pixel += pixelStride;
421 return Uint16Pair( start, end );
424 bool NinePatchImage::IsNinePatchUrl( const std::string& url )
428 std::string::const_reverse_iterator iter = url.rbegin();
429 enum { SUFFIX, HASH, HASH_DOT, DONE } state = SUFFIX;
430 while(iter < url.rend())
440 else if(!isalnum(*iter))
448 if( *iter == '#' || *iter == '9' )
464 state = DONE; // Stop testing characters
484 } // namespace Internal