From: Eunki Hong Date: Fri, 15 Oct 2021 07:05:58 +0000 (-0700) Subject: Apply EncodedImageBuffer with Atlas X-Git-Tag: dali_2.1.23~10^2 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fuifw%2Fdali-toolkit.git;a=commitdiff_plain;h=beb14983715858f026536290104c988b84685d85 Apply EncodedImageBuffer with Atlas Allow to use atlas with EncodedImageBuffer. Change-Id: Id98ac9507327215c8a8c4b6b04b3460214ac654d Signed-off-by: Eunki Hong --- diff --git a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp index e16e93e..10d6445 100644 --- a/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp +++ b/automated-tests/src/dali-toolkit/utc-Dali-ImageView.cpp @@ -784,9 +784,25 @@ int UtcDaliImageViewAsyncLoadingEncodedBufferWithAtlasing(void) 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); @@ -794,10 +810,15 @@ int UtcDaliImageViewAsyncLoadingEncodedBufferWithAtlasing(void) // 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(); @@ -848,6 +869,7 @@ int UtcDaliImageViewSyncLoadingEncodedBuffer(void) // 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; diff --git a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp index 31c3351..08b4cd0 100644 --- a/dali-toolkit/internal/image-loader/image-atlas-impl.cpp +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -33,8 +33,6 @@ namespace Toolkit { namespace Internal { -typedef unsigned char PixelBuffer; - Texture ImageAtlas::PackToAtlas(const std::vector& pixelData, Dali::Vector& textureRects) { // Record each block size @@ -68,7 +66,7 @@ Texture ImageAtlas::PackToAtlas(const std::vector& pixelData, Dali::V // Apply the half pixel correction to avoid the color bleeding between neighbour blocks textureRects[index].x = (static_cast(packPositionX) + 0.5f) / atlasWidth; // left - textureRects[index].y = (static_cast(packPositionY) + 0.5f) / atlasHeight; // right + textureRects[index].y = (static_cast(packPositionY) + 0.5f) / atlasHeight; // top textureRects[index].z = (static_cast(packPositionX + pixelData[index].GetWidth()) - 0.5f) / atlasWidth; // right textureRects[index].w = (static_cast(packPositionY + pixelData[index].GetHeight()) - 0.5f) / atlasHeight; // bottom } @@ -158,15 +156,15 @@ bool ImageAtlas::Upload(Vector4& textureRect, } } - 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(packPositionX) + 0.5f) / mWidth; // left - textureRect.y = (static_cast(packPositionY) + 0.5f) / mHeight; // right + textureRect.y = (static_cast(packPositionY) + 0.5f) / mHeight; // top textureRect.z = (static_cast(packPositionX + dimensions.GetX()) - 0.5f) / mWidth; // right textureRect.w = (static_cast(packPositionY + dimensions.GetY()) - 0.5f) / mHeight; // bottom @@ -183,17 +181,66 @@ bool ImageAtlas::Upload(Vector4& textureRect, 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(packPositionX) + 0.5f) / mWidth; // left + textureRect.y = (static_cast(packPositionY) + 0.5f) / mHeight; // top + textureRect.z = (static_cast(packPositionX + size.GetX()) - 0.5f) / mWidth; // right + textureRect.w = (static_cast(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(packPositionX) + 0.5f) / mWidth; // left - textureRect.y = (static_cast(packPositionY) + 0.5f) / mHeight; // right + textureRect.y = (static_cast(packPositionY) + 0.5f) / mHeight; // top textureRect.z = (static_cast(packPositionX + pixelData.GetWidth()) - 0.5f) / mWidth; // right textureRect.w = (static_cast(packPositionY + pixelData.GetHeight()) - 0.5f) / mHeight; // bottom @@ -228,7 +275,7 @@ void ImageAtlas::UploadToAtlas(uint32_t id, PixelData pixelData) { if(mLoadingTaskInfoContainer[0]->loadTaskId == id) { - Rect packRect(mLoadingTaskInfoContainer[0]->packRect); + Rect packRect(mLoadingTaskInfoContainer[0]->packRect); if(!pixelData || (pixelData.GetWidth() == 0 && pixelData.GetHeight() == 0)) { if(!mBrokenImageUrl.empty()) // replace with the broken image @@ -260,7 +307,7 @@ void ImageAtlas::UploadToAtlas(uint32_t id, PixelData pixelData) } } -void ImageAtlas::UploadBrokenImage(const Rect& area) +void ImageAtlas::UploadBrokenImage(const Rect& area) { Devel::PixelBuffer brokenBuffer = LoadImageFromFile(mBrokenImageUrl, ImageDimensions(area.width, area.height)); SizeType loadedWidth = brokenBuffer.GetWidth(); diff --git a/dali-toolkit/internal/image-loader/image-atlas-impl.h b/dali-toolkit/internal/image-loader/image-atlas-impl.h index eab1551..e302d92 100644 --- a/dali-toolkit/internal/image-loader/image-atlas-impl.h +++ b/dali-toolkit/internal/image-loader/image-atlas-impl.h @@ -2,7 +2,7 @@ #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. @@ -19,6 +19,7 @@ // EXTERNAL INCLUDES #include +#include #include #include #include @@ -26,8 +27,8 @@ // INTERNAL INCLUDES #include #include -#include #include +#include namespace Dali { @@ -86,6 +87,32 @@ public: 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); @@ -117,7 +144,7 @@ private: * * @param[in] area The pixel area for uploading. */ - void UploadBrokenImage(const Rect& area); + void UploadBrokenImage(const Rect& area); // Undefined ImageAtlas(const ImageAtlas& imageAtlas); @@ -132,11 +159,11 @@ private: */ 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), @@ -144,8 +171,8 @@ private: { } - unsigned short loadTaskId; - Rect packRect; + uint32_t loadTaskId; + Rect packRect; AtlasUploadObserver* observer; }; diff --git a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp index 880a075..1671f6e 100644 --- a/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp +++ b/dali-toolkit/internal/texture-manager/texture-manager-impl.cpp @@ -306,43 +306,48 @@ TextureSet TextureManager::LoadTexture( 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) @@ -354,7 +359,18 @@ TextureSet TextureManager::LoadTexture( 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 { diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.cpp b/dali-toolkit/internal/visuals/image-atlas-manager.cpp index 577a433..25fff39 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.cpp +++ b/dali-toolkit/internal/visuals/image-atlas-manager.cpp @@ -1,5 +1,5 @@ /* - * 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. @@ -84,7 +84,7 @@ TextureSet ImageAtlasManager::Add(Vector4& textureRect, } 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)) @@ -99,6 +99,34 @@ TextureSet ImageAtlasManager::Add(Vector4& textureRect, 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(size.GetWidth()) * static_cast(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) { @@ -108,7 +136,7 @@ TextureSet ImageAtlasManager::Add(Vector4& textureRect, 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)) @@ -125,7 +153,7 @@ TextureSet ImageAtlasManager::Add(Vector4& textureRect, 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) diff --git a/dali-toolkit/internal/visuals/image-atlas-manager.h b/dali-toolkit/internal/visuals/image-atlas-manager.h index d2630c1..ec7a402 100644 --- a/dali-toolkit/internal/visuals/image-atlas-manager.h +++ b/dali-toolkit/internal/visuals/image-atlas-manager.h @@ -2,7 +2,7 @@ #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. @@ -18,6 +18,7 @@ */ // EXTERNAL INCLUDES +#include #include #include #include @@ -82,6 +83,29 @@ public: 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 *