When a rasterize request occurs, create a task and rasterize on thread.
Change-Id: I8f9117490fb242c91d0124759e0a1f7df9ed701a
return true;
}
+ bool IsCanvasChanged() const
+ {
+ return true;
+ }
+
+ bool Rasterize()
+ {
+ return true;
+ }
+
bool AddDrawable(Dali::CanvasRenderer::Drawable& drawable)
{
if (!drawable)
return Internal::Adaptor::GetImplementation(*this).Commit();
}
+bool CanvasRenderer::IsCanvasChanged() const
+{
+ return Internal::Adaptor::GetImplementation(*this).IsCanvasChanged();
+}
+
+bool CanvasRenderer::Rasterize()
+{
+ return Internal::Adaptor::GetImplementation(*this).Rasterize();
+}
+
Devel::PixelBuffer CanvasRenderer::GetPixelBuffer()
{
return Internal::Adaptor::GetImplementation(*this).GetPixelBuffer();
#include <dali-toolkit-test-suite-utils.h>
#include <dali-toolkit/dali-toolkit.h>
#include <test-application.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <toolkit-event-thread-callback.h>
#include <dali-toolkit/devel-api/controls/canvas-view/canvas-view.h>
+#include <dali/devel-api/adaptor-framework/canvas-renderer.h>
#include <dali/devel-api/adaptor-framework/canvas-renderer-shape.h>
-
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h>
using namespace Dali;
using namespace Toolkit;
DALI_TEST_CHECK( canvasView );
application.GetScene().Add(canvasView);
- application.SendNotification();
- application.Render();
canvasView.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
DALI_TEST_CHECK( canvasView );
application.GetScene().Add(canvasView);
- application.SendNotification();
- application.Render();
canvasView.SetProperty(Actor::Property::SIZE, Vector2(-999, -999));
END_TEST;
}
+
+int UtcDaliCanvasViewRasterizeTaskP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeTaskGetCanvasViewP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ DALI_TEST_EQUALS( task->GetCanvasView(), dummyInternalCanvasView, TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeTaskGetBufferSizeP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ //There is no rasterized buffer.
+ DALI_TEST_EQUALS( task->GetBufferSize(), Vector2(0, 0), TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeTaskGetPixelDataP(void)
+{
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ DALI_TEST_EQUALS( task->GetPixelData(), PixelData(), TEST_LOCATION );
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadAddTaskP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task2 = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task2 );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+ dummyThread->AddTask(task2);
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadAddRemoveTaskP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+
+ dummyThread->RemoveTask(dummyInternalCanvasView);
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadApplyRasterizedP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+
+ dummyThread->ApplyRasterized();
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadTerminateThreadP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread::TerminateThread(dummyThread);
+
+ END_TEST;
+}
+
+PixelData CreatePixelData( unsigned int width, unsigned int height )
+{
+ unsigned int bufferSize = width*height*Pixel::GetBytesPerPixel( Pixel::RGBA8888 );
+
+ unsigned char* buffer= reinterpret_cast<unsigned char*>( malloc( bufferSize ) );
+ PixelData pixelData = PixelData::New( buffer, bufferSize, width, height, Pixel::RGBA8888, PixelData::FREE );
+
+ return pixelData;
+}
+
+int UtcDaliCanvasViewRasterizeThreadCallProcessP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+
+ dummyThread->Process(false);
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewRasterizeThreadRasterizationCompletedSignalP(void)
+{
+ ToolkitTestApplication application;
+
+ Dali::Toolkit::Internal::CanvasView* dummyInternalCanvasView = new Dali::Toolkit::Internal::CanvasView(Vector2(100,100));
+ DALI_TEST_CHECK( dummyInternalCanvasView );
+
+ Dali::CanvasRenderer dummyCanvasRenderer = Dali::CanvasRenderer::New(Vector2(100, 100));
+ DALI_TEST_CHECK( dummyCanvasRenderer );
+
+ IntrusivePtr<Dali::Toolkit::Internal::CanvasRendererRasterizingTask> task = new Dali::Toolkit::Internal::CanvasRendererRasterizingTask(dummyInternalCanvasView, dummyCanvasRenderer);
+ DALI_TEST_CHECK( task );
+
+ Dali::Toolkit::Internal::CanvasViewRasterizeThread *dummyThread = new Dali::Toolkit::Internal::CanvasViewRasterizeThread();
+ DALI_TEST_CHECK( dummyThread );
+
+ dummyThread->AddTask(task);
+
+ dummyThread->Process(false);
+
+ PixelData pixelData = CreatePixelData( 100, 100 );
+
+ dummyThread->RasterizationCompletedSignal().Connect(dummyInternalCanvasView, &Dali::Toolkit::Internal::CanvasView::ApplyRasterizedImage);
+ dummyThread->RasterizationCompletedSignal().Emit(pixelData);
+
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
+
+int UtcDaliCanvasViewSetSizeAndAddDrawable(void)
+{
+ ToolkitTestApplication application;
+
+ CanvasView canvasView = CanvasView::New(Vector2(100,100));
+ DALI_TEST_CHECK( canvasView );
+
+ application.GetScene().Add(canvasView);
+
+ canvasView.SetProperty(Actor::Property::SIZE, Vector2(300, 300));
+
+ application.SendNotification();
+ application.Render();
+
+ Dali::CanvasRenderer::Shape shape = Dali::CanvasRenderer::Shape::New();
+
+ shape.AddRect(Rect<float>(10, 10, 10, 10), Vector2(0, 0));
+
+ canvasView.AddDrawable(shape);
+
+ application.SendNotification();
+ application.Render();
+
+ DALI_TEST_EQUALS( Test::WaitForEventThreadTrigger( 1 ), true, TEST_LOCATION );
+
+ application.SendNotification();
+ application.Render();
+
+ END_TEST;
+}
#include <dali/public-api/object/type-registry.h>
// INTERNAL INCLUDES
-#include <dali-toolkit/devel-api/controls/canvas-view/canvas-view.h>
#include <dali-toolkit/devel-api/controls/control-devel.h>
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-rasterize-thread.h>
#include <dali-toolkit/internal/controls/control/control-data-impl.h>
#include <dali-toolkit/internal/graphics/builtin-shader-extern-gen.h>
#include <dali-toolkit/internal/visuals/visual-factory-cache.h>
: Control(ControlBehaviour(CONTROL_BEHAVIOUR_DEFAULT)),
mCanvasRenderer(CanvasRenderer::New(viewBox)),
mTexture(),
- mChanged(false)
+ mTextureSet(),
+ mSize(viewBox),
+ mCanvasViewRasterizeThread(nullptr)
{
}
CanvasView::~CanvasView()
{
+ if(mCanvasViewRasterizeThread)
+ {
+ mCanvasViewRasterizeThread->RemoveTask(this);
+
+ CanvasViewRasterizeThread::TerminateThread(mCanvasViewRasterizeThread);
+ }
+
if(Adaptor::IsAvailable())
{
Adaptor::Get().UnregisterProcessor(*this);
void CanvasView::OnRelayout(const Vector2& size, RelayoutContainer& container)
{
if(!mCanvasRenderer ||
- mCanvasRenderer.GetSize() == size ||
!mCanvasRenderer.SetSize(size))
{
return;
}
- mChanged = true;
+ mSize = size;
}
void CanvasView::OnSizeSet(const Vector3& targetSize)
Control::OnSizeSet(targetSize);
if(!mCanvasRenderer ||
- mCanvasRenderer.GetSize() == Vector2(targetSize) ||
!mCanvasRenderer.SetSize(Vector2(targetSize)))
{
return;
}
- mChanged = true;
+ mSize.width = targetSize.width;
+ mSize.height = targetSize.height;
}
void CanvasView::Process(bool postProcessor)
{
- if(!mCanvasRenderer)
+ if(mCanvasRenderer && mCanvasRenderer.IsCanvasChanged() && mSize.width > 0 && mSize.height > 0)
{
- return;
+ AddRasterizationTask();
}
- Commit();
}
-void CanvasView::Commit()
+void CanvasView::AddRasterizationTask()
{
- if(mCanvasRenderer && mCanvasRenderer.Commit())
+ CanvasRendererRasterizingTaskPtr newTask = new CanvasRendererRasterizingTask(this, mCanvasRenderer);
+
+ if(!mCanvasViewRasterizeThread)
{
- Devel::PixelBuffer pixbuf = mCanvasRenderer.GetPixelBuffer();
- auto width = pixbuf.GetWidth();
- auto height = pixbuf.GetHeight();
+ mCanvasViewRasterizeThread = new CanvasViewRasterizeThread();
+ mCanvasViewRasterizeThread->RasterizationCompletedSignal().Connect(this, &CanvasView::ApplyRasterizedImage);
+ mCanvasViewRasterizeThread->Start();
+ }
- Dali::PixelData pixelData = Devel::PixelBuffer::Convert(pixbuf);
- if(!pixelData)
- {
- return;
- }
+ if(mCanvasRenderer.Commit())
+ {
+ mCanvasViewRasterizeThread->AddTask(newTask);
+ }
+}
- if(!mTexture || mChanged)
- {
- mTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, width, height);
- mTexture.Upload(pixelData);
- TextureSet textureSet = TextureSet::New();
- textureSet.SetTexture(0, mTexture);
- Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
- Shader shader = Shader::New(SHADER_CANVAS_VIEW_VERT, SHADER_CANVAS_VIEW_FRAG);
- Renderer renderer = Renderer::New(geometry, shader);
- renderer.SetTextures(textureSet);
- renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
-
- Self().AddRenderer(renderer);
- mChanged = false;
- }
- else
+void CanvasView::ApplyRasterizedImage(PixelData rasterizedPixelData)
+{
+ if(rasterizedPixelData)
+ {
+ auto rasterizedPixelDataWidth = rasterizedPixelData.GetWidth();
+ auto rasterizedPixelDataHeight = rasterizedPixelData.GetHeight();
+
+ if(rasterizedPixelDataWidth > 0 && rasterizedPixelDataHeight > 0)
{
- //Update texture
- mTexture.Upload(pixelData);
+ if(!mTexture || mTexture.GetWidth() != rasterizedPixelDataWidth || mTexture.GetHeight() != rasterizedPixelDataHeight)
+ {
+ mTexture = Texture::New(TextureType::TEXTURE_2D, Dali::Pixel::RGBA8888, rasterizedPixelDataWidth, rasterizedPixelDataHeight);
+ mTexture.Upload(rasterizedPixelData);
+
+ if(!mTextureSet)
+ {
+ mTextureSet = TextureSet::New();
+ Geometry geometry = VisualFactoryCache::CreateQuadGeometry();
+ Shader shader = Shader::New(SHADER_CANVAS_VIEW_VERT, SHADER_CANVAS_VIEW_FRAG);
+ Renderer renderer = Renderer::New(geometry, shader);
+ renderer.SetTextures(mTextureSet);
+ renderer.SetProperty(Renderer::Property::BLEND_PRE_MULTIPLIED_ALPHA, true);
+
+ Actor actor = Self();
+ if(actor)
+ {
+ actor.AddRenderer(renderer);
+ }
+ }
+ mTextureSet.SetTexture(0, mTexture);
+ }
+ else
+ {
+ //Update texture
+ mTexture.Upload(rasterizedPixelData);
+ }
}
}
+
+ //If there are accumulated changes to CanvasRenderer during Rasterize, Rasterize once again.
+ if(mCanvasRenderer && mCanvasRenderer.IsCanvasChanged())
+ {
+ AddRasterizationTask();
+ }
}
bool CanvasView::AddDrawable(Dali::CanvasRenderer::Drawable& drawable)
{
namespace Toolkit
{
-class CanvasView;
-
namespace Internal
{
+class CanvasView;
+class CanvasViewRasterizeThread;
+
class CanvasView : public Control, public Integration::Processor
{
public:
*/
void OnInitialize() override;
-protected: // Implementation of Processor
+ /**
+ * @bried Rasterize the canvas, and add it to the view.
+ *
+ * @param[in] size The target size of the canvas view rasterization.
+ */
+ void AddRasterizationTask();
+
+protected:
/**
* @copydoc Dali::Integration::Processor::Process()
*/
void Process(bool postProcessor) override;
-private:
+public:
/**
- * @brief Draw drawables added to CanvasView on inner canvas.
- * Then make that buffer into a texture and add it to renderer.
+ * @bried Apply the rasterized image to the canvas view
+ *
+ * @param[in] rasterizedPixelData The pixel buffer with the rasterized pixels
*/
- void Commit();
+ void ApplyRasterizedImage(PixelData rasterizedPixelData);
private:
CanvasView(const CanvasView&) = delete;
CanvasView& operator=(const CanvasView&) = delete;
private:
- CanvasRenderer mCanvasRenderer;
- Dali::Texture mTexture;
- bool mChanged;
+ CanvasRenderer mCanvasRenderer;
+ Dali::Texture mTexture;
+ TextureSet mTextureSet;
+ Vector2 mSize;
+ CanvasViewRasterizeThread* mCanvasViewRasterizeThread;
};
} // namespace Internal
--- /dev/null
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include "canvas-view-rasterize-thread.h"
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/thread-settings.h>
+#include <dali/integration-api/adaptor-framework/adaptor.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+CanvasRendererRasterizingTask::CanvasRendererRasterizingTask(CanvasView* canvasView, CanvasRenderer canvasRenderer)
+: mCanvasView(canvasView),
+ mCanvasRenderer(canvasRenderer),
+ mPixelData(PixelData()),
+ mBufferSize(0, 0)
+{
+}
+
+bool CanvasRendererRasterizingTask::Rasterize()
+{
+ if(mCanvasRenderer && mCanvasRenderer.Rasterize())
+ {
+ Devel::PixelBuffer pixbuf = mCanvasRenderer.GetPixelBuffer();
+ auto width = pixbuf.GetWidth();
+ auto height = pixbuf.GetHeight();
+ if(width > 0 && height > 0)
+ {
+ mBufferSize.width = width;
+ mBufferSize.height = height;
+
+ mPixelData = Devel::PixelBuffer::Convert(pixbuf);
+ return true;
+ }
+ }
+ return false;
+}
+
+CanvasView* CanvasRendererRasterizingTask::GetCanvasView() const
+{
+ return mCanvasView.Get();
+}
+
+PixelData CanvasRendererRasterizingTask::GetPixelData() const
+{
+ return mPixelData;
+}
+
+Vector2 CanvasRendererRasterizingTask::GetBufferSize() const
+{
+ return mBufferSize;
+}
+
+CanvasViewRasterizeThread::CanvasViewRasterizeThread()
+: mTrigger(new EventThreadCallback(MakeCallback(this, &CanvasViewRasterizeThread::ApplyRasterized))),
+ mLogFactory(Dali::Adaptor::Get().GetLogFactory()),
+ mProcessorRegistered(false),
+ mRasterizationCompletedSignal()
+{
+}
+
+CanvasViewRasterizeThread::~CanvasViewRasterizeThread()
+{
+ if(mProcessorRegistered && Adaptor::IsAvailable())
+ {
+ Adaptor::Get().UnregisterProcessor(*this);
+ }
+}
+
+void CanvasViewRasterizeThread::TerminateThread(CanvasViewRasterizeThread*& thread)
+{
+ if(thread)
+ {
+ // add an empty task would stop the thread from conditional wait.
+ thread->AddTask(CanvasRendererRasterizingTaskPtr());
+ // stop the thread
+ thread->Join();
+ // delete the thread
+ delete thread;
+ thread = NULL;
+ }
+}
+
+void CanvasViewRasterizeThread::AddTask(CanvasRendererRasterizingTaskPtr task)
+{
+ bool wasEmpty = false;
+
+ {
+ // Lock while adding task to the queue
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+ wasEmpty = mRasterizeTasks.empty();
+ if(!wasEmpty && task != NULL)
+ {
+ // Remove the tasks with the same renderer.
+ // Older task which waiting to rasterize and apply the svg to the same renderer is expired.
+ for(std::vector<CanvasRendererRasterizingTaskPtr>::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it)
+ {
+ if((*it) && (*it)->GetCanvasView() == task->GetCanvasView()) //Need
+ {
+ mRasterizeTasks.erase(it);
+ break;
+ }
+ }
+ }
+ mRasterizeTasks.push_back(task);
+
+ if(!mProcessorRegistered && Adaptor::IsAvailable())
+ {
+ Adaptor::Get().RegisterProcessor(*this);
+ mProcessorRegistered = true;
+ }
+ }
+
+ if(wasEmpty)
+ {
+ // wake up the image loading thread
+ mConditionalWait.Notify();
+ }
+}
+
+CanvasRendererRasterizingTaskPtr CanvasViewRasterizeThread::NextCompletedTask()
+{
+ // Lock while popping task out from the queue
+ Mutex::ScopedLock lock(mMutex);
+
+ if(mCompletedTasks.empty())
+ {
+ return CanvasRendererRasterizingTaskPtr();
+ }
+
+ std::vector<CanvasRendererRasterizingTaskPtr>::iterator next = mCompletedTasks.begin();
+ CanvasRendererRasterizingTaskPtr nextTask = *next;
+ mCompletedTasks.erase(next);
+
+ return nextTask;
+}
+
+void CanvasViewRasterizeThread::RemoveTask(CanvasView* canvasView)
+{
+ // Lock while remove task from the queue
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+ if(!mRasterizeTasks.empty())
+ {
+ for(std::vector<CanvasRendererRasterizingTaskPtr>::iterator it = mRasterizeTasks.begin(), endIt = mRasterizeTasks.end(); it != endIt; ++it)
+ {
+ if((*it) && (*it)->GetCanvasView() == canvasView)
+ {
+ mRasterizeTasks.erase(it);
+ break;
+ }
+ }
+ }
+
+ UnregisterProcessor();
+}
+
+CanvasRendererRasterizingTaskPtr CanvasViewRasterizeThread::NextTaskToProcess()
+{
+ // Lock while popping task out from the queue
+ ConditionalWait::ScopedLock lock(mConditionalWait);
+
+ // conditional wait
+ while(mRasterizeTasks.empty())
+ {
+ mConditionalWait.Wait(lock);
+ }
+
+ // pop out the next task from the queue
+ std::vector<CanvasRendererRasterizingTaskPtr>::iterator next = mRasterizeTasks.begin();
+ CanvasRendererRasterizingTaskPtr nextTask = *next;
+ mRasterizeTasks.erase(next);
+
+ return nextTask;
+}
+
+void CanvasViewRasterizeThread::AddCompletedTask(CanvasRendererRasterizingTaskPtr task)
+{
+ // Lock while adding task to the queue
+ Mutex::ScopedLock lock(mMutex);
+ mCompletedTasks.push_back(task);
+
+ // wake up the main thread
+ mTrigger->Trigger();
+}
+
+void CanvasViewRasterizeThread::Run()
+{
+ SetThreadName("CanvasViewThread");
+ mLogFactory.InstallLogFunction();
+
+ while(CanvasRendererRasterizingTaskPtr task = NextTaskToProcess())
+ {
+ if(task->Rasterize())
+ {
+ AddCompletedTask(task);
+ }
+ }
+}
+
+void CanvasViewRasterizeThread::ApplyRasterized()
+{
+ while(CanvasRendererRasterizingTaskPtr task = NextCompletedTask())
+ {
+ RasterizationCompletedSignal().Emit(task->GetPixelData());
+ }
+
+ UnregisterProcessor();
+}
+
+void CanvasViewRasterizeThread::Process(bool postProcessor)
+{
+ ApplyRasterized();
+}
+
+CanvasViewRasterizeThread::RasterizationCompletedSignalType& CanvasViewRasterizeThread::RasterizationCompletedSignal()
+{
+ return mRasterizationCompletedSignal;
+}
+
+void CanvasViewRasterizeThread::UnregisterProcessor()
+{
+ if(mProcessorRegistered)
+ {
+ if(mRasterizeTasks.empty() && mCompletedTasks.empty() && Adaptor::IsAvailable())
+ {
+ Adaptor::Get().UnregisterProcessor(*this);
+ mProcessorRegistered = false;
+ }
+ }
+}
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
--- /dev/null
+#ifndef DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H
+#define DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H
+
+/*
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// EXTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/canvas-renderer.h>
+#include <dali/devel-api/adaptor-framework/event-thread-callback.h>
+#include <dali/devel-api/threading/conditional-wait.h>
+#include <dali/devel-api/threading/mutex.h>
+#include <dali/devel-api/threading/thread.h>
+#include <dali/integration-api/adaptor-framework/log-factory-interface.h>
+#include <dali/public-api/common/intrusive-ptr.h>
+#include <dali/public-api/images/pixel-data.h>
+#include <dali/public-api/object/ref-object.h>
+#include <dali/public-api/rendering/texture-set.h>
+#include <memory>
+#include <vector>
+
+// INTERNAL INCLUDES
+#include <dali-toolkit/internal/controls/canvas-view/canvas-view-impl.h>
+
+namespace Dali
+{
+namespace Toolkit
+{
+namespace Internal
+{
+using CanvasViewPtr = IntrusivePtr<CanvasView>;
+class CanvasRendererRasterizingTask;
+using CanvasRendererRasterizingTaskPtr = IntrusivePtr<CanvasRendererRasterizingTask>;
+
+/**
+ * The canvasview rasterizing tasks to be processed in the worker thread.
+ *
+ * Life cycle of a rasterizing task is as follows:
+ * 1. Created by CanvasView in the main thread
+ * 2. Queued in the worked thread waiting to be processed.
+ * 3. If this task gets its turn to do the rasterization, it triggers main thread to apply the rasterized image to material then been deleted in main thread call back.
+ * Or if this task is been removed before its turn to be processed, it then been deleted in the worker thread.
+ */
+class CanvasRendererRasterizingTask : public RefObject
+{
+public:
+ /**
+ * Constructor
+ * @param[in] canvasRenderer The renderer which the rasterized canvas to be applied.
+ */
+ CanvasRendererRasterizingTask(CanvasView* canvasView, CanvasRenderer canvasRenderer);
+
+ /**
+ * Destructor.
+ */
+ ~CanvasRendererRasterizingTask() = default;
+
+ /**
+ * Do the rasterization with the mRasterizer.
+ * @return Returns True when it's successful. False otherwise.
+ */
+ bool Rasterize();
+
+ /**
+ * Get the CanvasView
+ * @return The CanvasView pointer.
+ */
+ CanvasView* GetCanvasView() const;
+
+ /**
+ * Get the rasterization result.
+ * @return The pixel data with the rasterized pixels.
+ */
+ PixelData GetPixelData() const;
+
+ /**
+ * Get size of rasterization result.
+ * @return The size of the pixel data.
+ */
+ Vector2 GetBufferSize() const;
+
+private:
+ // Undefined
+ CanvasRendererRasterizingTask(const CanvasRendererRasterizingTask& task);
+
+ // Undefined
+ CanvasRendererRasterizingTask& operator=(const CanvasRendererRasterizingTask& task);
+
+private:
+ CanvasViewPtr mCanvasView;
+ CanvasRenderer mCanvasRenderer;
+ PixelData mPixelData;
+ Vector2 mBufferSize;
+};
+
+/**
+ * The worker thread for CanvasView rasterization.
+ */
+class CanvasViewRasterizeThread : public Thread, Integration::Processor
+{
+public:
+ /// @brief ApplyRasterizedImage Event signal type
+ using RasterizationCompletedSignalType = Signal<void(PixelData)>;
+
+public:
+ /**
+ * Constructor.
+ *
+ * @param[in] trigger The trigger to wake up the main thread.
+ */
+ CanvasViewRasterizeThread();
+
+ /**
+ * Terminate the CanvasView rasterize thread, join and delete.
+ *
+ * @param[in] thread The rasterize thread.
+ */
+ static void TerminateThread(CanvasViewRasterizeThread*& thread);
+
+ /**
+ * Add a rasterization task into the waiting queue, called by main thread.
+ *
+ * @param[in] task The task added to the queue.
+ */
+ void AddTask(CanvasRendererRasterizingTaskPtr task);
+
+ /**
+ * Remove the task with the given CanvasView from the waiting queue, called by main thread.
+ *
+ * Typically called when the actor is put off stage, so the renderer is not needed anymore.
+ *
+ * @param[in] canvasView The CanvasView pointer.
+ */
+ void RemoveTask(CanvasView* canvasView);
+
+ /**
+ * Applies the rasterized image to material
+ */
+ void ApplyRasterized();
+
+ /**
+ * @copydoc Dali::Integration::Processor::Process()
+ */
+ void Process(bool postProcessor) override;
+
+ /**
+ * @brief This signal is emitted when rasterized image is applied.
+ *
+ * @return The signal to connect to
+ */
+ RasterizationCompletedSignalType& RasterizationCompletedSignal();
+
+private:
+ /**
+ * Pop the next task out from the queue.
+ *
+ * @return The next task to be processed.
+ */
+ CanvasRendererRasterizingTaskPtr NextTaskToProcess();
+
+ /**
+ * Pop the next task out from the completed queue, called by main thread.
+ *
+ * @return The next task in the completed queue.
+ */
+ CanvasRendererRasterizingTaskPtr NextCompletedTask();
+
+ /**
+ * Add a task in to the queue
+ *
+ * @param[in] task The task added to the queue.
+ */
+ void AddCompletedTask(CanvasRendererRasterizingTaskPtr task);
+
+ /**
+ * @brief Unregister a previously registered processor
+ *
+ */
+ void UnregisterProcessor();
+
+protected:
+ /**
+ * Destructor.
+ */
+ ~CanvasViewRasterizeThread() override;
+
+ /**
+ * The entry function of the worker thread.
+ * It fetches task from the Queue, rasterizes the image and apply to the renderer.
+ */
+ void Run() override;
+
+private:
+ // Undefined
+ CanvasViewRasterizeThread(const CanvasViewRasterizeThread& thread);
+
+ // Undefined
+ CanvasViewRasterizeThread& operator=(const CanvasViewRasterizeThread& thread);
+
+private:
+ std::vector<CanvasRendererRasterizingTaskPtr> mRasterizeTasks; //The queue of the tasks waiting to rasterize the CanvasView.
+ std::vector<CanvasRendererRasterizingTaskPtr> mCompletedTasks; //The queue of the tasks with the SVG rasterization completed
+
+ ConditionalWait mConditionalWait;
+ Dali::Mutex mMutex;
+ std::unique_ptr<EventThreadCallback> mTrigger;
+ const Dali::LogFactoryInterface& mLogFactory;
+ bool mProcessorRegistered;
+ RasterizationCompletedSignalType mRasterizationCompletedSignal;
+};
+
+} // namespace Internal
+
+} // namespace Toolkit
+
+} // namespace Dali
+
+#endif // DALI_TOOLKIT_CANVAS_VIEW_RASTERIZE_THREAD_H
${toolkit_src_dir}/controls/buttons/radio-button-impl.cpp
${toolkit_src_dir}/controls/buttons/toggle-button-impl.cpp
${toolkit_src_dir}/controls/canvas-view/canvas-view-impl.cpp
+ ${toolkit_src_dir}/controls/canvas-view/canvas-view-rasterize-thread.cpp
${toolkit_src_dir}/controls/control/control-data-impl.cpp
${toolkit_src_dir}/controls/control/control-debug.cpp
${toolkit_src_dir}/controls/control/control-renderers.cpp