SVG support for ImageView ( CPU rendering version )
[platform/core/uifw/dali-toolkit.git] / dali-toolkit / internal / image-atlas / image-atlas-impl.cpp
1 /*
2  * Copyright (c) 2015 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 "image-atlas-impl.h"
20
21 // EXTERNAL INCLUDES
22 #include <string.h>
23 #include <dali/public-api/signals/callback.h>
24 #include <dali/public-api/images/resource-image.h>
25 #include <dali/integration-api/debug.h>
26
27 namespace Dali
28 {
29
30 namespace Toolkit
31 {
32
33 namespace Internal
34 {
35
36 ImageAtlas::ImageAtlas( SizeType width, SizeType height, Pixel::Format pixelFormat )
37 : mPacker( width, height ),
38   mLoadQueue(),
39   mCompleteQueue( new EventThreadCallback( MakeCallback( this, &ImageAtlas::UploadToAtlas ) ) ),
40   mLoadingThread( mLoadQueue, mCompleteQueue ),
41   mBrokenImageUrl(""),
42   mBrokenImageSize(),
43   mPixelFormat( pixelFormat ),
44   mLoadingThreadStarted( false )
45 {
46   mAtlas = Atlas::New( width, height, pixelFormat );
47   mWidth = static_cast<float>(width);
48   mHeight = static_cast<float>( height );
49 }
50
51 ImageAtlas::~ImageAtlas()
52 {
53   if( mLoadingThreadStarted )
54   {
55     // add an empty task would stop the loading thread from contional wait.
56     mLoadQueue.AddTask( NULL );
57     // stop the loading thread
58     mLoadingThread.Join();
59     // The atlas can still be used as texture after ImageAtlas has been thrown away,
60     // so make sure all the loaded bitmap been uploaded to atlas
61     UploadToAtlas();
62   }
63 }
64
65 IntrusivePtr<ImageAtlas> ImageAtlas::New( SizeType width, SizeType height, Pixel::Format pixelFormat )
66 {
67   IntrusivePtr<ImageAtlas> internal = new ImageAtlas( width, height, pixelFormat );
68   return internal;
69 }
70
71 Image ImageAtlas::GetAtlas()
72 {
73   return mAtlas;
74 }
75
76 void ImageAtlas::SetBrokenImage( const std::string& brokenImageUrl )
77 {
78   mBrokenImageSize = ResourceImage::GetImageSize( brokenImageUrl );
79   if(mBrokenImageSize.GetWidth() > 0 && mBrokenImageSize.GetHeight() > 0 ) // check the url is valid
80   {
81     mBrokenImageUrl = brokenImageUrl;
82   }
83 }
84
85 bool ImageAtlas::Upload( Vector4& textureRect,
86                          const std::string& url,
87                          ImageDimensions size,
88                          FittingMode::Type fittingMode,
89                          bool orientationCorrection )
90 {
91   ImageDimensions dimensions = size;
92   ImageDimensions zero;
93   if( size == zero ) // image size not provided
94   {
95     dimensions = ResourceImage::GetImageSize( url );
96     if( dimensions == zero ) // Fail to read the image & broken image file exists
97     {
98       if( !mBrokenImageUrl.empty() )
99       {
100         return Upload( textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true );
101       }
102       else
103       {
104         textureRect = Vector4::ZERO;
105         return true;
106       }
107     }
108   }
109
110   if( static_cast<unsigned int>(dimensions.GetWidth() * dimensions.GetHeight()) > mPacker.GetAvailableArea() )
111   {
112     return false;
113   }
114
115   unsigned int packPositionX = 0;
116   unsigned int packPositionY = 0;
117   if( mPacker.Pack( dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY ) )
118   {
119     if( !mLoadingThreadStarted )
120     {
121       mLoadingThread.Start();
122       mLoadingThreadStarted = true;
123     }
124
125     LoadingTask* newTask = new LoadingTask(BitmapLoader::New(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection ),
126                                            packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight());
127     mLoadQueue.AddTask( newTask );
128
129     // apply the half pixel correction
130     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
131     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
132     textureRect.z = ( static_cast<float>( packPositionX + dimensions.GetX() )-0.5f ) / mWidth; // right
133     textureRect.w = ( static_cast<float>( packPositionY + dimensions.GetY() )-0.5f ) / mHeight;// bottom
134
135     return true;
136   }
137
138   return false;
139 }
140
141 bool ImageAtlas::Upload( Vector4& textureRect, PixelDataPtr pixelData )
142 {
143   unsigned int packPositionX = 0;
144   unsigned int packPositionY = 0;
145   if( mPacker.Pack( pixelData->GetWidth(), pixelData->GetHeight(), packPositionX, packPositionY ) )
146   {
147     mAtlas.Upload( pixelData, packPositionX, packPositionY );
148
149     // apply the half pixel correction
150     textureRect.x = ( static_cast<float>( packPositionX ) +0.5f ) / mWidth; // left
151     textureRect.y = ( static_cast<float>( packPositionY ) +0.5f ) / mHeight; // right
152     textureRect.z = ( static_cast<float>( packPositionX + pixelData->GetWidth() )-0.5f ) / mWidth; // right
153     textureRect.w = ( static_cast<float>( packPositionY + pixelData->GetHeight() )-0.5f ) / mHeight;// bottom
154
155     return true;
156   }
157
158   return false;
159 }
160
161 void ImageAtlas::Remove( const Vector4& textureRect )
162 {
163   mPacker.DeleteBlock( static_cast<SizeType>(textureRect.x*mWidth),
164                        static_cast<SizeType>(textureRect.y*mHeight),
165                        static_cast<SizeType>((textureRect.z-textureRect.x)*mWidth+1.f),
166                        static_cast<SizeType>((textureRect.w-textureRect.y)*mHeight+1.f) );
167 }
168
169 void ImageAtlas::UploadToAtlas()
170 {
171   while( LoadingTask* next = mCompleteQueue.NextTask() )
172   {
173     if( ! next->loader.IsLoaded() )
174     {
175       if(!mBrokenImageUrl.empty()) // replace with the broken image
176       {
177         UploadBrokenImage( next->packRect );
178       }
179
180       DALI_LOG_ERROR( "Failed to load the image: %s\n", (next->loader.GetUrl()).c_str());
181     }
182     else
183     {
184       if( next->loader.GetPixelData()->GetWidth() < next->packRect.width || next->loader.GetPixelData()->GetHeight() < next->packRect.height  )
185       {
186         DALI_LOG_ERROR( "Can not upscale the image from actual loaded size [ %d, %d ] to specified size [ %d, %d ]\n",
187                         next->loader.GetPixelData()->GetWidth(),
188                         next->loader.GetPixelData()->GetHeight(),
189                         next->packRect.width,
190                         next->packRect.height );
191       }
192
193       mAtlas.Upload( next->loader.GetPixelData(), next->packRect.x, next->packRect.y );
194     }
195
196     delete next;
197   }
198 }
199
200 void ImageAtlas::UploadBrokenImage( const Rect<SizeType>& area )
201 {
202   BitmapLoader loader = BitmapLoader::New(mBrokenImageUrl, ImageDimensions( area.width, area.height ) );
203   loader.Load();
204   SizeType loadedWidth = loader.GetPixelData()->GetWidth();
205   SizeType loadedHeight = loader.GetPixelData()->GetHeight();
206
207   bool needBackgroundClear = false;
208   SizeType packX = area.x;
209   SizeType packY = area.y;
210   // locate the broken image in the middle.
211   if( area.width > loadedWidth)
212   {
213     packX += (area.width - loadedWidth)/2;
214     needBackgroundClear = true;
215   }
216   if( area.height > loadedHeight)
217   {
218     packY += (area.height - loadedHeight)/2;
219     needBackgroundClear = true;
220   }
221
222   if( needBackgroundClear )
223   {
224     SizeType size = area.width * area.height * Pixel::GetBytesPerPixel( mPixelFormat );
225     PixelBuffer* buffer = new PixelBuffer [size];
226     PixelDataPtr background = PixelData::New( buffer, area.width, area.height, mPixelFormat, PixelData::DELETE_ARRAY );
227     for( SizeType idx = 0; idx < size; idx++ )
228     {
229       buffer[idx] = 0x00;
230     }
231     mAtlas.Upload( background, area.x, area.y );
232   }
233
234   mAtlas.Upload( loader.GetPixelData(), packX, packY );
235 }
236
237 } // namespace Internal
238
239 } // namespace Toolkit
240
241 } // namespace Dali