Allow to use atlas with EncodedImageBuffer.
Change-Id: Id98ac9507327215c8a8c4b6b04b3460214ac654d
Signed-off-by: Eunki Hong <eunkiki.hong@samsung.com>
Property::Map imageMap;
imageMap[ImageVisual::Property::URL] = url.GetUrl();
+ imageMap[ImageVisual::Property::DESIRED_HEIGHT] = 600;
+ imageMap[ImageVisual::Property::DESIRED_WIDTH] = 600;
+ imageMap[ImageVisual::Property::ATLASING] = true;
+
+ // No atlasing with big image
+ ImageView imageView_bigdesired = ImageView::New();
+ imageView_bigdesired.SetProperty(ImageView::Property::IMAGE, imageMap);
+ imageView_bigdesired.SetProperty(Toolkit::Control::Property::PADDING, Extents(10u, 10u, 10u, 10u));
+
+ imageMap[ImageVisual::Property::DESIRED_HEIGHT] = 0;
+ imageMap[ImageVisual::Property::DESIRED_WIDTH] = 0;
+
+ // No atlasing with zero desired size
+ ImageView imageView_nodesired = ImageView::New();
+ imageView_nodesired.SetProperty(ImageView::Property::IMAGE, imageMap);
+ imageView_nodesired.SetProperty(Toolkit::Control::Property::PADDING, Extents(10u, 10u, 10u, 10u));
+
imageMap[ImageVisual::Property::DESIRED_HEIGHT] = 34;
imageMap[ImageVisual::Property::DESIRED_WIDTH] = 34;
- imageMap[ImageVisual::Property::ATLASING] = true;
ImageView imageView = ImageView::New();
imageView.SetProperty(ImageView::Property::IMAGE, imageMap);
// By default, Aysnc loading is used
// loading is not started if the actor is offScene
-
application.GetScene().Add(imageView);
+ application.GetScene().Add(imageView_bigdesired);
+ application.GetScene().Add(imageView_nodesired);
application.SendNotification();
application.Render(16);
+
+ // loading started, this waits for the loader thread for max 30 seconds
+ DALI_TEST_EQUALS(Test::WaitForEventThreadTrigger(1), true, TEST_LOCATION);
+
application.Render(16);
application.SendNotification();
// Sync loading is used
Property::Map syncLoadingMap;
syncLoadingMap["url"] = url.GetUrl();
+ syncLoadingMap["alphaMaskUrl"] = gImage_34_RGBA;
syncLoadingMap["desiredHeight"] = 34;
syncLoadingMap["desiredWidth"] = 34;
syncLoadingMap["synchronousLoading"] = true;
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
{
namespace Internal
{
-typedef unsigned char PixelBuffer;
-
Texture ImageAtlas::PackToAtlas(const std::vector<PixelData>& pixelData, Dali::Vector<Vector4>& textureRects)
{
// Record each block size
// Apply the half pixel correction to avoid the color bleeding between neighbour blocks
textureRects[index].x = (static_cast<float>(packPositionX) + 0.5f) / atlasWidth; // left
- textureRects[index].y = (static_cast<float>(packPositionY) + 0.5f) / atlasHeight; // right
+ textureRects[index].y = (static_cast<float>(packPositionY) + 0.5f) / atlasHeight; // top
textureRects[index].z = (static_cast<float>(packPositionX + pixelData[index].GetWidth()) - 0.5f) / atlasWidth; // right
textureRects[index].w = (static_cast<float>(packPositionY + pixelData[index].GetHeight()) - 0.5f) / atlasHeight; // bottom
}
}
}
- unsigned int packPositionX = 0;
- unsigned int packPositionY = 0;
+ uint32_t packPositionX = 0;
+ uint32_t packPositionY = 0;
if(mPacker.Pack(dimensions.GetWidth(), dimensions.GetHeight(), packPositionX, packPositionY))
{
- unsigned short loadId = GetImplementation(mAsyncLoader).Load(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF);
+ uint32_t loadId = GetImplementation(mAsyncLoader).Load(url, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF);
mLoadingTaskInfoContainer.PushBack(new LoadingTaskInfo(loadId, packPositionX, packPositionY, dimensions.GetWidth(), dimensions.GetHeight(), atlasUploadObserver));
// apply the half pixel correction
textureRect.x = (static_cast<float>(packPositionX) + 0.5f) / mWidth; // left
- textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight; // right
+ textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight; // top
textureRect.z = (static_cast<float>(packPositionX + dimensions.GetX()) - 0.5f) / mWidth; // right
textureRect.w = (static_cast<float>(packPositionY + dimensions.GetY()) - 0.5f) / mHeight; // bottom
return false;
}
+bool ImageAtlas::Upload(Vector4& textureRect,
+ const EncodedImageBuffer& encodedImageBuffer,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver)
+{
+ ImageDimensions zero;
+ if(size == zero) // image size not provided
+ {
+ DALI_LOG_ERROR("Desired size is zero! We need to setup desired size for Atlas.\n");
+ // EncodedImageBuffer didn't support to get closest image size.
+ // Just draw broken image.
+ if(!mBrokenImageUrl.empty())
+ {
+ return Upload(textureRect, mBrokenImageUrl, mBrokenImageSize, FittingMode::DEFAULT, true, atlasUploadObserver);
+ }
+ else
+ {
+ textureRect = Vector4::ZERO;
+ return true;
+ }
+ }
+
+ uint32_t packPositionX = 0;
+ uint32_t packPositionY = 0;
+ if(mPacker.Pack(size.GetWidth(), size.GetHeight(), packPositionX, packPositionY))
+ {
+ uint32_t loadId = GetImplementation(mAsyncLoader).LoadEncodedImageBuffer(encodedImageBuffer, size, fittingMode, SamplingMode::BOX_THEN_LINEAR, orientationCorrection, DevelAsyncImageLoader::PreMultiplyOnLoad::OFF);
+ mLoadingTaskInfoContainer.PushBack(new LoadingTaskInfo(loadId, packPositionX, packPositionY, size.GetWidth(), size.GetHeight(), atlasUploadObserver));
+ // apply the half pixel correction
+ textureRect.x = (static_cast<float>(packPositionX) + 0.5f) / mWidth; // left
+ textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight; // top
+ textureRect.z = (static_cast<float>(packPositionX + size.GetX()) - 0.5f) / mWidth; // right
+ textureRect.w = (static_cast<float>(packPositionY + size.GetY()) - 0.5f) / mHeight; // bottom
+
+ if(atlasUploadObserver)
+ {
+ // register to the observer,
+ // Not that a matching unregister call should be invoked in UploadToAtlas if the observer is still alive by then.
+ atlasUploadObserver->Register(*this);
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
bool ImageAtlas::Upload(Vector4& textureRect, PixelData pixelData)
{
- unsigned int packPositionX = 0;
- unsigned int packPositionY = 0;
+ uint32_t packPositionX = 0;
+ uint32_t packPositionY = 0;
if(mPacker.Pack(pixelData.GetWidth(), pixelData.GetHeight(), packPositionX, packPositionY))
{
mAtlas.Upload(pixelData, 0u, 0u, packPositionX, packPositionY, pixelData.GetWidth(), pixelData.GetHeight());
// apply the half pixel correction
textureRect.x = (static_cast<float>(packPositionX) + 0.5f) / mWidth; // left
- textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight; // right
+ textureRect.y = (static_cast<float>(packPositionY) + 0.5f) / mHeight; // top
textureRect.z = (static_cast<float>(packPositionX + pixelData.GetWidth()) - 0.5f) / mWidth; // right
textureRect.w = (static_cast<float>(packPositionY + pixelData.GetHeight()) - 0.5f) / mHeight; // bottom
{
if(mLoadingTaskInfoContainer[0]->loadTaskId == id)
{
- Rect<unsigned int> packRect(mLoadingTaskInfoContainer[0]->packRect);
+ Rect<uint32_t> packRect(mLoadingTaskInfoContainer[0]->packRect);
if(!pixelData || (pixelData.GetWidth() == 0 && pixelData.GetHeight() == 0))
{
if(!mBrokenImageUrl.empty()) // replace with the broken image
}
}
-void ImageAtlas::UploadBrokenImage(const Rect<unsigned int>& area)
+void ImageAtlas::UploadBrokenImage(const Rect<uint32_t>& area)
{
Devel::PixelBuffer brokenBuffer = LoadImageFromFile(mBrokenImageUrl, ImageDimensions(area.width, area.height));
SizeType loadedWidth = brokenBuffer.GetWidth();
#define DALI_TOOLKIT_IMAGE_ATLAS_IMPL_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
// EXTERNAL INCLUDES
#include <dali/devel-api/common/owner-container.h>
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
#include <dali/public-api/common/intrusive-ptr.h>
#include <dali/public-api/object/base-object.h>
#include <dali/public-api/signals/connection-tracker.h>
// INTERNAL INCLUDES
#include <dali-toolkit/devel-api/image-loader/image-atlas.h>
#include <dali-toolkit/internal/image-loader/atlas-packer.h>
-#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
#include <dali-toolkit/internal/visuals/visual-url.h>
+#include <dali-toolkit/public-api/image-loader/async-image-loader.h>
namespace Dali
{
AtlasUploadObserver* atlasUploadObserver);
/**
+ * @brief Upload a resource image to the atlas by encoded buffer.
+ *
+ * @note To make the atlasing efficient, a valid size should be provided.
+ * If size is not provided, then SegFault occured.
+ * Do not set a size that is bigger than the actual image size, as the up-scaling is not available,
+ * the content of the area not covered by actual image is undefined, it will not be cleared.
+ *
+ * SamplingMode::BOX_THEN_LINEAR is used to sampling pixels from the input image while fitting it to desired size.
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] encodedImageBuffer The encoded raw buffer of the resource image file to use.
+ * @param [in] size The width and height to fit the loaded image to.
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param [in] atlasUploadObserver The observer to observe the upload state inside the ImageAtlas.
+ * @return True if there is enough space to fit this image in,false otherwise.
+ * @note The valid callback function here is required to have the signature of void( void ).
+ */
+ bool Upload(Vector4& textureRect,
+ const EncodedImageBuffer& encodedImageBuffer,
+ ImageDimensions size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver);
+
+ /**
* @copydoc Toolkit::ImageAtlas::Upload( Vector4&, PixelData )
*/
bool Upload(Vector4& textureRect, PixelData pixelData);
*
* @param[in] area The pixel area for uploading.
*/
- void UploadBrokenImage(const Rect<unsigned int>& area);
+ void UploadBrokenImage(const Rect<uint32_t>& area);
// Undefined
ImageAtlas(const ImageAtlas& imageAtlas);
*/
struct LoadingTaskInfo
{
- LoadingTaskInfo(unsigned short loadTaskId,
- unsigned int packPositionX,
- unsigned int packPositionY,
- unsigned int width,
- unsigned int height,
+ LoadingTaskInfo(uint32_t loadTaskId,
+ uint32_t packPositionX,
+ uint32_t packPositionY,
+ uint32_t width,
+ uint32_t height,
AtlasUploadObserver* observer)
: loadTaskId(loadTaskId),
packRect(packPositionX, packPositionY, width, height),
{
}
- unsigned short loadTaskId;
- Rect<unsigned int> packRect;
+ uint32_t loadTaskId;
+ Rect<uint32_t> packRect;
AtlasUploadObserver* observer;
};
else
{
// For Atlas
- if(synchronousLoading && atlasingStatus && imageAtlasManager->CheckAtlasAvailable(url, desiredSize))
+ if(synchronousLoading && atlasingStatus)
{
- Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
-
- if(maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
+ const bool synchronousAtlasAvaliable = (desiredSize != ImageDimensions() || url.IsLocalResource()) ? imageAtlasManager->CheckAtlasAvailable(url, desiredSize)
+ : false;
+ if(synchronousAtlasAvaliable)
{
- Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl.GetUrl(), ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
- if(maskPixelBuffer)
+ Devel::PixelBuffer pixelBuffer = LoadImageSynchronously(url, desiredSize, fittingMode, samplingMode, orientationCorrection);
+
+ if(pixelBuffer && maskInfo && maskInfo->mAlphaMaskUrl.IsValid())
{
- pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
+ Devel::PixelBuffer maskPixelBuffer = LoadImageSynchronously(maskInfo->mAlphaMaskUrl, ImageDimensions(), FittingMode::SCALE_TO_FILL, SamplingMode::NO_FILTER, true);
+ if(maskPixelBuffer)
+ {
+ pixelBuffer.ApplyMask(maskPixelBuffer, maskInfo->mContentScaleFactor, maskInfo->mCropToMask);
+ }
}
- }
-
- PixelData data;
- if(pixelBuffer)
- {
- PreMultiply(pixelBuffer, preMultiplyOnLoad);
- data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
- if(data)
+ PixelData data;
+ if(pixelBuffer)
{
- textureSet = imageAtlasManager->Add(textureRect, data);
- if(textureSet)
+ PreMultiply(pixelBuffer, preMultiplyOnLoad);
+ data = Devel::PixelBuffer::Convert(pixelBuffer); // takes ownership of buffer
+
+ if(data)
+ {
+ textureSet = imageAtlasManager->Add(textureRect, data);
+ if(textureSet)
+ {
+ textureRectSize.SetWidth(data.GetWidth());
+ textureRectSize.SetHeight(data.GetHeight());
+ }
+ }
+ else
{
- textureRectSize.SetWidth(data.GetWidth());
- textureRectSize.SetHeight(data.GetHeight());
+ DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
}
}
- else
+ if(!textureSet)
{
- DALI_LOG_ERROR("TextureManager::LoadTexture: Synchronous Texture loading with atlasing is failed.\n");
+ atlasingStatus = false;
}
}
- if(!textureSet)
- {
- atlasingStatus = false;
- }
}
if(!textureSet)
Dali::ImageDimensions atlasDesiredSize = desiredSize;
if(atlasingStatus)
{
- textureSet = imageAtlasManager->Add(textureRect, url.GetUrl(), atlasDesiredSize, fittingMode, true, atlasObserver);
+ if(url.IsBufferResource())
+ {
+ const EncodedImageBuffer& encodedImageBuffer = GetEncodedImageBuffer(url.GetUrl());
+ if(encodedImageBuffer)
+ {
+ textureSet = imageAtlasManager->Add(textureRect, encodedImageBuffer, desiredSize, fittingMode, true, atlasObserver);
+ }
+ }
+ else
+ {
+ textureSet = imageAtlasManager->Add(textureRect, url, atlasDesiredSize, fittingMode, true, atlasObserver);
+ }
}
if(!textureSet) // big image, no atlasing or atlasing failed
{
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
}
size = dimensions;
- unsigned int i = 0;
+ uint32_t i = 0;
for(AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
{
if(GetImplementation(*iter).Upload(textureRect, url, size, fittingMode, orientationCorrection, atlasUploadObserver))
return mTextureSetList.back();
}
+TextureSet ImageAtlasManager::Add(Vector4& textureRect,
+ const EncodedImageBuffer& encodedImageBuffer,
+ const ImageDimensions& size,
+ FittingMode::Type fittingMode,
+ bool orientationCorrection,
+ AtlasUploadObserver* atlasUploadObserver)
+{
+ // big image, atlasing is not applied
+ if(static_cast<uint32_t>(size.GetWidth()) * static_cast<uint32_t>(size.GetHeight()) > MAX_ITEM_AREA || size.GetWidth() > DEFAULT_ATLAS_SIZE || size.GetHeight() > DEFAULT_ATLAS_SIZE)
+ {
+ return TextureSet();
+ }
+
+ uint32_t i = 0;
+ for(AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
+ {
+ if(GetImplementation(*iter).Upload(textureRect, encodedImageBuffer, size, fittingMode, orientationCorrection, atlasUploadObserver))
+ {
+ return mTextureSetList[i];
+ }
+ i++;
+ }
+
+ CreateNewAtlas();
+ GetImplementation(mAtlasList.back()).Upload(textureRect, encodedImageBuffer, size, fittingMode, orientationCorrection, atlasUploadObserver);
+ return mTextureSetList.back();
+}
+
TextureSet ImageAtlasManager::Add(Vector4& textureRect,
PixelData pixelData)
{
return TextureSet();
}
- unsigned int i = 0;
+ uint32_t i = 0;
for(AtlasContainer::iterator iter = mAtlasList.begin(); iter != mAtlasList.end(); ++iter)
{
if((*iter).Upload(textureRect, pixelData))
void ImageAtlasManager::Remove(TextureSet textureSet, const Vector4& textureRect)
{
- unsigned int i = 0;
+ uint32_t i = 0;
for(TextureSetContainer::iterator iter = mTextureSetList.begin(); iter != mTextureSetList.end(); ++iter)
{
if((*iter) == textureSet)
#define DALI_TOOLKIT_IMAGE_ATLAS_MANAGER_H
/*
- * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2022 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.
*/
// EXTERNAL INCLUDES
+#include <dali/public-api/adaptor-framework/encoded-image-buffer.h>
#include <dali/public-api/common/vector-wrapper.h>
#include <dali/public-api/object/ref-object.h>
#include <dali/public-api/rendering/texture-set.h>
FittingMode::Type fittingMode = FittingMode::DEFAULT,
bool orientationCorrection = true,
AtlasUploadObserver* atlasUploadObserver = NULL);
+
+ /**
+ * @brief Add an image to the atlas.
+ *
+ * @note To make the atlasing efficient, an valid size should be provided.
+ *
+ * SamplingMode::BOX_THEN_LINEAR is used to sampling pixels from the input image while fitting it to desired size.
+ *
+ * @param [out] textureRect The texture area of the resource image in the atlas.
+ * @param [in] encodedImageBuffer The encoded buffer of the resource image file to use.
+ * @param [in] size The width and height to fit the loaded image to.
+ * @param [in] fittingMode The method used to fit the shape of the image before loading to the shape defined by the size parameter.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ * @param [in] atlasUploadObserver The object to observe the uploading state inside ImageAtlas.
+ * @return The texture set containing the image.
+ */
+ TextureSet Add(Vector4& textureRect,
+ const EncodedImageBuffer& encodedImageBuffer,
+ const ImageDimensions& size,
+ FittingMode::Type fittingMode = FittingMode::DEFAULT,
+ bool orientationCorrection = true,
+ AtlasUploadObserver* atlasUploadObserver = NULL);
+
/**
* @brief Add a pixel buffer to the atlas
*