Support YUV decoding for JPEG 26/272226/8
authorHeeyong Song <heeyong.song@samsung.com>
Thu, 3 Feb 2022 07:10:18 +0000 (16:10 +0900)
committerHeeyong Song <heeyong.song@samsung.com>
Mon, 18 Apr 2022 08:11:39 +0000 (17:11 +0900)
Change-Id: Ie7fddb22e70159821c27fb2ca1ccc2db19fe1ec4

automated-tests/resources/lake_front.jpg [new file with mode: 0644]
automated-tests/src/dali-adaptor/utc-Dali-ImageLoading.cpp
dali/devel-api/adaptor-framework/image-loader-input.h
dali/devel-api/adaptor-framework/image-loading.cpp
dali/devel-api/adaptor-framework/image-loading.h
dali/internal/imaging/common/image-loader.cpp
dali/internal/imaging/common/image-loader.h
dali/internal/imaging/common/loader-jpeg-turbo.cpp
dali/internal/imaging/common/loader-jpeg.h
dali/internal/imaging/common/pixel-manipulation.cpp

diff --git a/automated-tests/resources/lake_front.jpg b/automated-tests/resources/lake_front.jpg
new file mode 100644 (file)
index 0000000..470a679
Binary files /dev/null and b/automated-tests/resources/lake_front.jpg differ
index 1019707..12af8b0 100644 (file)
@@ -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.
@@ -34,6 +34,9 @@ const char* IMAGE_128_RGB = TEST_RESOURCE_DIR "/gallery-small-1.jpg";
 // resolution: 2000*2560, pixel format: RGB888
 const char* IMAGE_LARGE_EXIF3_RGB = TEST_RESOURCE_DIR "/f-large-exif-3.jpg";
 
+// resolution: 2048*2048, pixel format: RGB888, YUV420
+const char* IMAGE_LARGE_2048_YUV_420 = TEST_RESOURCE_DIR "/lake_front.jpg";
+
 // resolution: 55*64, pixel format: RGB888
 const char* IMAGE_WIDTH_ODD_EXIF1_RGB = TEST_RESOURCE_DIR "/f-odd-exif-1.jpg";
 // resolution: 55*64, pixel format: RGB888
@@ -74,7 +77,7 @@ const char* IMAGENONEXIST = "non-exist.jpg";
 Dali::Vector<uint8_t> FileToMemory(const char* filename)
 {
   Dali::Vector<uint8_t> buffer;
-  FILE *fp;
+  FILE*                 fp;
   fp = fopen(filename, "rb");
   if(fp != NULL)
   {
@@ -418,3 +421,46 @@ int UtcDaliDownloadImageN(void)
 
   END_TEST;
 }
+
+int UtcDaliLoadImagePlanesFromFileP(void)
+{
+  std::vector<Devel::PixelBuffer> pixelBuffers;
+
+  Dali::LoadImagePlanesFromFile(IMAGE_LARGE_2048_YUV_420, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 3, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 2048u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 2048u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // Test not supported image format: png
+  Dali::LoadImagePlanesFromFile(IMAGE_34_RGBA, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 34u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 34u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::RGBA8888, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // Test notsupported chrominace subsampling case
+  Dali::LoadImagePlanesFromFile(IMAGE_128_RGB, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 1, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::RGB888, TEST_LOCATION);
+
+  END_TEST;
+}
+
+int UtcDaliLoadImagePlanesFromFileN(void)
+{
+  std::vector<Devel::PixelBuffer> pixelBuffers;
+
+  Dali::LoadImagePlanesFromFile(IMAGENONEXIST, pixelBuffers);
+  DALI_TEST_CHECK(pixelBuffers.empty());
+
+  END_TEST;
+}
index 830b33c..80d7b61 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TIZEN_PLATFORM_IMAGE_LOADER_INPUT_H
 
 /*
- * Copyright (c) 2020 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.
@@ -63,6 +63,7 @@ struct Input
 };
 
 using LoadBitmapFunction       = bool (*)(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& pixelData);
+using LoadPlanesFunction       = bool (*)(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
 using LoadBitmapHeaderFunction = bool (*)(const Dali::ImageLoader::Input& input, unsigned int& width, unsigned int& height);
 
 /**
@@ -70,12 +71,13 @@ using LoadBitmapHeaderFunction = bool (*)(const Dali::ImageLoader::Input& input,
  */
 struct BitmapLoader
 {
-  unsigned char                      magicByte1; ///< The first byte in the file should be this
-  unsigned char                      magicByte2; ///< The second byte in the file should be this
-  LoadBitmapFunction                 loader;     ///< The function which decodes the file
-  LoadBitmapHeaderFunction           header;     ///< The function which decodes the header of the file
-  Dali::Integration::Bitmap::Profile profile;    ///< The kind of bitmap to be created
-                                                 ///  (addressable packed pixels or an opaque compressed blob).
+  unsigned char                      magicByte1;    ///< The first byte in the file should be this
+  unsigned char                      magicByte2;    ///< The second byte in the file should be this
+  LoadBitmapFunction                 loader;        ///< The function which decodes the file
+  LoadPlanesFunction                 planeLoader;   ///< The function which decodes the file to each plane
+  LoadBitmapHeaderFunction           header;        ///< The function which decodes the header of the file
+  Dali::Integration::Bitmap::Profile profile;       ///< The kind of bitmap to be created
+                                                    ///  (addressable packed pixels or an opaque compressed blob).
 };
 
 } // namespace ImageLoader
index cfaaa12..207b79a 100644 (file)
@@ -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.
@@ -51,6 +51,18 @@ Devel::PixelBuffer LoadImageFromFile(const std::string& url, ImageDimensions siz
   return Dali::Devel::PixelBuffer();
 }
 
+void LoadImagePlanesFromFile(const std::string& url, std::vector<Devel::PixelBuffer>& buffers, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection)
+{
+  Integration::BitmapResourceType resourceType(size, fittingMode, samplingMode, orientationCorrection);
+
+  Internal::Platform::FileReader fileReader(url);
+  FILE* const                    fp = fileReader.GetFile();
+  if(fp != NULL)
+  {
+    TizenPlatform::ImageLoader::ConvertStreamToPlanes(resourceType, url, fp, buffers);
+  }
+}
+
 Devel::PixelBuffer LoadImageFromBuffer(const Dali::Vector<uint8_t>& buffer, ImageDimensions size, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, bool orientationCorrection)
 {
   if(buffer.Empty())
@@ -66,7 +78,7 @@ Devel::PixelBuffer LoadImageFromBuffer(const Dali::Vector<uint8_t>& buffer, Imag
   {
     Dali::Devel::PixelBuffer bitmap;
     // Make path as empty string. Path information just for file format hint.
-    bool                     success = TizenPlatform::ImageLoader::ConvertStreamToBitmap(resourceType, std::string(""), fp, bitmap);
+    bool success = TizenPlatform::ImageLoader::ConvertStreamToBitmap(resourceType, std::string(""), fp, bitmap);
     if(success && bitmap)
     {
       return bitmap;
index c1f7ad8..53e07df 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_IMAGE_LOADING_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.
@@ -47,6 +47,27 @@ DALI_ADAPTOR_API Devel::PixelBuffer LoadImageFromFile(
   bool               orientationCorrection = true);
 
 /**
+ * @brief Load an image and save each plane to a separate buffer synchronously from local file.
+ *
+ * @note This method is thread safe, i.e. can be called from any thread.
+ *       If the image file doesn't support to load planes, this method returns a bitmap image instead.
+ *
+ * @param [in] url The URL of the image file to load.
+ * @param [out] buffers The loaded PixelBuffer object list or an empty list in case loading failed.
+ * @param [in] size The width and height to fit the loaded image to, 0.0 means whole image
+ * @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] samplingMode The filtering method used when sampling pixels from the input image while fitting it to desired size.
+ * @param [in] orientationCorrection Reorient the image to respect any orientation metadata in its header.
+ */
+DALI_ADAPTOR_API void LoadImagePlanesFromFile(
+  const std::string&               url,
+  std::vector<Devel::PixelBuffer>& buffers,
+  ImageDimensions                  size                  = ImageDimensions(0, 0),
+  FittingMode::Type                fittingMode           = FittingMode::DEFAULT,
+  SamplingMode::Type               samplingMode          = SamplingMode::BOX_THEN_LINEAR,
+  bool                             orientationCorrection = true);
+
+/**
  * @brief Load an image synchronously from encoded buffer.
  *
  * @note This method is thread safe, i.e. can be called from any thread.
index 8c7139d..9f9db11 100644 (file)
@@ -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.
@@ -80,15 +80,15 @@ enum FileFormats
 // clang-format off
 const Dali::ImageLoader::BitmapLoader BITMAP_LOADER_LOOKUP_TABLE[FORMAT_TOTAL_COUNT] =
   {
-    {Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  LoadPngHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED      },
-    {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED      },
-    {Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
-    {0x0,                0x0,                LoadBitmapFromWbmp, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Png::MAGIC_BYTE_1,  Png::MAGIC_BYTE_2,  LoadBitmapFromPng,  nullptr, LoadPngHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Jpeg::MAGIC_BYTE_1, Jpeg::MAGIC_BYTE_2, LoadBitmapFromJpeg, LoadPlanesFromJpeg, LoadJpegHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Bmp::MAGIC_BYTE_1,  Bmp::MAGIC_BYTE_2,  LoadBitmapFromBmp,  nullptr, LoadBmpHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Gif::MAGIC_BYTE_1,  Gif::MAGIC_BYTE_2,  LoadBitmapFromGif,  nullptr, LoadGifHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Webp::MAGIC_BYTE_1, Webp::MAGIC_BYTE_2, LoadBitmapFromWebp, nullptr, LoadWebpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {Ktx::MAGIC_BYTE_1,  Ktx::MAGIC_BYTE_2,  LoadBitmapFromKtx,  nullptr, LoadKtxHeader,  Bitmap::BITMAP_COMPRESSED      },
+    {Astc::MAGIC_BYTE_1, Astc::MAGIC_BYTE_2, LoadBitmapFromAstc, nullptr, LoadAstcHeader, Bitmap::BITMAP_COMPRESSED      },
+    {Ico::MAGIC_BYTE_1,  Ico::MAGIC_BYTE_2,  LoadBitmapFromIco,  nullptr, LoadIcoHeader,  Bitmap::BITMAP_2D_PACKED_PIXELS},
+    {0x0,                0x0,                LoadBitmapFromWbmp, nullptr, LoadWbmpHeader, Bitmap::BITMAP_2D_PACKED_PIXELS},
   };
 // clang-format on
 
@@ -151,6 +151,7 @@ FileFormats GetFormatHint(const std::string& filename)
 bool GetBitmapLoaderFunctions(FILE*                                        fp,
                               FileFormats                                  format,
                               Dali::ImageLoader::LoadBitmapFunction&       loader,
+                              Dali::ImageLoader::LoadPlanesFunction&       planeLoader,
                               Dali::ImageLoader::LoadBitmapHeaderFunction& header,
                               Bitmap::Profile&                             profile,
                               const std::string&                           filename)
@@ -238,9 +239,10 @@ bool GetBitmapLoaderFunctions(FILE*                                        fp,
   // if a loader was found set the outputs
   if(loaderFound)
   {
-    loader  = lookupPtr->loader;
-    header  = lookupPtr->header;
-    profile = lookupPtr->profile;
+    loader      = lookupPtr->loader;
+    planeLoader = lookupPtr->planeLoader;
+    header      = lookupPtr->header;
+    profile     = lookupPtr->profile;
   }
 
   // Reset to the start of the file.
@@ -256,7 +258,7 @@ bool GetBitmapLoaderFunctions(FILE*                                        fp,
 
 namespace ImageLoader
 {
-bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
+bool ConvertStreamToBitmap(const BitmapResourceType& resource, const std::string& path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer)
 {
   DALI_LOG_TRACE_METHOD(gLogFilter);
 
@@ -265,6 +267,7 @@ bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path,
   if(fp != NULL)
   {
     Dali::ImageLoader::LoadBitmapFunction       function;
+    Dali::ImageLoader::LoadPlanesFunction       planeLoader;
     Dali::ImageLoader::LoadBitmapHeaderFunction header;
 
     Bitmap::Profile profile;
@@ -272,6 +275,7 @@ bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path,
     if(GetBitmapLoaderFunctions(fp,
                                 GetFormatHint(path),
                                 function,
+                                planeLoader,
                                 header,
                                 profile,
                                 path))
@@ -299,6 +303,73 @@ bool ConvertStreamToBitmap(const BitmapResourceType& resource, std::string path,
   return result;
 }
 
+bool ConvertStreamToPlanes(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers)
+{
+  DALI_LOG_TRACE_METHOD(gLogFilter);
+
+  bool result = false;
+
+  if(fp != NULL)
+  {
+    Dali::ImageLoader::LoadBitmapFunction       loader;
+    Dali::ImageLoader::LoadPlanesFunction       planeLoader;
+    Dali::ImageLoader::LoadBitmapHeaderFunction header;
+
+    Bitmap::Profile profile;
+
+    if(GetBitmapLoaderFunctions(fp,
+                                GetFormatHint(path),
+                                loader,
+                                planeLoader,
+                                header,
+                                profile,
+                                path))
+    {
+      const Dali::ImageLoader::ScalingParameters scalingParameters(resource.size, resource.scalingMode, resource.samplingMode);
+      const Dali::ImageLoader::Input             input(fp, scalingParameters, resource.orientationCorrection);
+
+      pixelBuffers.clear();
+
+      // Run the image type decoder:
+      if(planeLoader)
+      {
+        result = planeLoader(input, pixelBuffers);
+        if(!result)
+        {
+          DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
+        }
+      }
+      else
+      {
+        Dali::Devel::PixelBuffer pixelBuffer;
+        result = loader(input, pixelBuffer);
+        if(!result)
+        {
+          DALI_LOG_ERROR("Unable to convert %s\n", path.c_str());
+          return false;
+        }
+
+        pixelBuffer = Internal::Platform::ApplyAttributesToBitmap(pixelBuffer, resource.size, resource.scalingMode, resource.samplingMode);
+        if(pixelBuffer)
+        {
+          pixelBuffers.push_back(pixelBuffer);
+        }
+        else
+        {
+          DALI_LOG_ERROR("ApplyAttributesToBitmap is failed [%s]\n", path.c_str());
+          return false;
+        }
+      }
+    }
+    else
+    {
+      DALI_LOG_ERROR("Image Decoder for %s unavailable\n", path.c_str());
+    }
+  }
+
+  return result;
+}
+
 ResourcePointer LoadImageSynchronously(const Integration::BitmapResourceType& resource, const std::string& path)
 {
   ResourcePointer          result;
@@ -349,12 +420,14 @@ ImageDimensions GetClosestImageSize(const std::string& filename,
   if(fp != NULL)
   {
     Dali::ImageLoader::LoadBitmapFunction       loaderFunction;
+    Dali::ImageLoader::LoadPlanesFunction       planeLoader;
     Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
     Bitmap::Profile                             profile;
 
     if(GetBitmapLoaderFunctions(fp,
                                 GetFormatHint(filename),
                                 loaderFunction,
+                                planeLoader,
                                 headerFunction,
                                 profile,
                                 filename))
@@ -398,12 +471,14 @@ ImageDimensions GetClosestImageSize(Integration::ResourcePointer resourceBuffer,
       if(fp != NULL)
       {
         Dali::ImageLoader::LoadBitmapFunction       loaderFunction;
+        Dali::ImageLoader::LoadPlanesFunction       planeLoader;
         Dali::ImageLoader::LoadBitmapHeaderFunction headerFunction;
         Bitmap::Profile                             profile;
 
         if(GetBitmapLoaderFunctions(fp,
                                     FORMAT_UNKNOWN,
                                     loaderFunction,
+                                    planeLoader,
                                     headerFunction,
                                     profile,
                                     ""))
index f63b186..b0d490f 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TIZEN_PLATFORM_IMAGE_LOADER_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.
@@ -44,16 +44,18 @@ namespace ImageLoader
  * @param[out] bitmap Pointer to write bitmap to
  * @return true on success, false on failure
  */
-bool ConvertStreamToBitmap(const Integration::BitmapResourceType& resource, std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
+bool ConvertStreamToBitmap(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
 
 /**
- * Convert a bitmap and write to a file stream.
+ * Convert a file stream into image planes.
+ * @param[in] resource The resource to convert.
  * @param[in] path The path to the resource.
  * @param[in] fp File Pointer. Closed on exit.
- * @param[out] pixelData Reference to PixelData object.
+ * @param[out] pixelBuffers Pointer to write buffer to
  * @return true on success, false on failure
+ * @note If the image file doesn't support to load planes, this method returns one RGB bitmap image.
  */
-bool ConvertBitmapToStream(std::string path, FILE* const fp, Dali::Devel::PixelBuffer& pixelBuffer);
+bool ConvertStreamToPlanes(const Integration::BitmapResourceType& resource, const std::string& path, FILE* const fp, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
 
 /**
  * Loads an image synchronously
index 0571582..dc39f24 100644 (file)
@@ -113,6 +113,22 @@ bool IsJpegErrorFatal(const std::string& errorMessage)
   return true;
 }
 
+bool IsJpegDecodingFailed()
+{
+  std::string errorString = tjGetErrorStr();
+
+  if(DALI_UNLIKELY(IsJpegErrorFatal(errorString)))
+  {
+    DALI_LOG_ERROR("%s\n", errorString.c_str());
+    return true;
+  }
+  else
+  {
+    DALI_LOG_WARNING("%s\n", errorString.c_str());
+    return false;
+  }
+}
+
 // helpers for safe exif memory handling
 using ExifHandle = std::unique_ptr<ExifData, decltype(exif_data_free)*>;
 
@@ -476,12 +492,202 @@ void Rotate270(PixelArray buffer, int width, int height)
   }
 }
 
+void GetJpegPixelFormat(int jpegColorspace, TJPF& pixelLibJpegType, Pixel::Format& pixelFormat)
+{
+  pixelLibJpegType = TJPF_RGB;
+  pixelFormat      = Pixel::RGB888;
+
+  switch(jpegColorspace)
+  {
+    case TJCS_RGB:
+    // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission.
+    // YCbCr images must be converted to RGB before they can actually be displayed.
+    case TJCS_YCbCr:
+    {
+      pixelLibJpegType = TJPF_RGB;
+      pixelFormat      = Pixel::RGB888;
+      break;
+    }
+    case TJCS_GRAY:
+    {
+      pixelLibJpegType = TJPF_GRAY;
+      pixelFormat      = Pixel::L8;
+      break;
+    }
+    case TJCS_CMYK:
+    case TJCS_YCCK:
+    {
+      pixelLibJpegType = TJPF_CMYK;
+      pixelFormat      = Pixel::RGBA8888;
+      break;
+    }
+    default:
+    {
+      pixelLibJpegType = TJPF_RGB;
+      pixelFormat      = Pixel::RGB888;
+      break;
+    }
+  }
+}
+
+bool TransformBitmap(int scaledPreXformWidth, int scaledPreXformHeight, JpegTransform transform, uint8_t* bitmapPixelBuffer, Pixel::Format pixelFormat)
+{
+  const unsigned int bufferWidth  = Dali::TizenPlatform::GetTextureDimension(scaledPreXformWidth);
+  const unsigned int bufferHeight = Dali::TizenPlatform::GetTextureDimension(scaledPreXformHeight);
+
+  bool result = false;
+
+  switch(transform)
+  {
+    case JpegTransform::NONE:
+    {
+      result = true;
+      break;
+    }
+    // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
+    case JpegTransform::ROTATE_180:
+    {
+      static auto rotate180Functions = TransformFunctionArray{
+        &Rotate180<1>,
+        &Rotate180<3>,
+        &Rotate180<4>,
+      };
+      result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    case JpegTransform::ROTATE_270:
+    {
+      static auto rotate270Functions = TransformFunctionArray{
+        &Rotate270<1>,
+        &Rotate270<3>,
+        &Rotate270<4>,
+      };
+      result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    case JpegTransform::ROTATE_90:
+    {
+      static auto rotate90Functions = TransformFunctionArray{
+        &Rotate90<1>,
+        &Rotate90<3>,
+        &Rotate90<4>,
+      };
+      result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    case JpegTransform::FLIP_VERTICAL:
+    {
+      static auto flipVerticalFunctions = TransformFunctionArray{
+        &FlipVertical<1>,
+        &FlipVertical<3>,
+        &FlipVertical<4>,
+      };
+      result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    // Less-common orientation changes, since they don't correspond to a camera's physical orientation:
+    case JpegTransform::FLIP_HORIZONTAL:
+    {
+      static auto flipHorizontalFunctions = TransformFunctionArray{
+        &FlipHorizontal<1>,
+        &FlipHorizontal<3>,
+        &FlipHorizontal<4>,
+      };
+      result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    case JpegTransform::TRANSPOSE:
+    {
+      static auto transposeFunctions = TransformFunctionArray{
+        &Transpose<1>,
+        &Transpose<3>,
+        &Transpose<4>,
+      };
+      result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    case JpegTransform::TRANSVERSE:
+    {
+      static auto transverseFunctions = TransformFunctionArray{
+        &Transverse<1>,
+        &Transverse<3>,
+        &Transverse<4>,
+      };
+      result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
+      break;
+    }
+    default:
+    {
+      DALI_LOG_ERROR("Unsupported JPEG Orientation transformation: %x.\n", transform);
+      break;
+    }
+  }
+  return result;
+}
+
+bool LoadJpegFile(const Dali::ImageLoader::Input& input, Vector<uint8_t>& jpegBuffer, unsigned int& jpegBufferSize)
+{
+  FILE* const fp = input.file;
+
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
+  {
+    DALI_LOG_ERROR("Error seeking to end of file\n");
+    return false;
+  }
+
+  long positionIndicator = ftell(fp);
+  jpegBufferSize         = 0u;
+  if(positionIndicator > -1L)
+  {
+    jpegBufferSize = static_cast<unsigned int>(positionIndicator);
+  }
+
+  if(DALI_UNLIKELY(0u == jpegBufferSize))
+  {
+    DALI_LOG_ERROR("Jpeg buffer size error\n");
+    return false;
+  }
+
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
+  {
+    DALI_LOG_ERROR("Error seeking to start of file\n");
+    return false;
+  }
+
+  try
+  {
+    jpegBuffer.ResizeUninitialized(jpegBufferSize);
+  }
+  catch(...)
+  {
+    DALI_LOG_ERROR("Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U);
+    return false;
+  }
+  unsigned char* const jpegBufferPtr = jpegBuffer.Begin();
+
+  // Pull the compressed JPEG image bytes out of a file and into memory:
+  if(DALI_UNLIKELY(fread(jpegBufferPtr, 1, jpegBufferSize, fp) != jpegBufferSize))
+  {
+    DALI_LOG_ERROR("Error on image file read.\n");
+    return false;
+  }
+
+  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
+  {
+    DALI_LOG_ERROR("Error seeking to start of file\n");
+    return false;
+  }
+
+  return true;
+}
+
 } // namespace
 
 namespace Dali
 {
 namespace TizenPlatform
 {
+bool          DecodeJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers, bool decodeToYuv);
 JpegTransform ConvertExifOrientation(ExifData* exifData);
 bool          TransformSize(int requiredWidth, int requiredHeight, FittingMode::Type fittingMode, SamplingMode::Type samplingMode, JpegTransform transform, int& preXformImageWidth, int& preXformImageHeight, int& postXformImageWidth, int& postXformImageHeight);
 
@@ -530,68 +736,45 @@ bool LoadJpegHeader(FILE* fp, unsigned int& width, unsigned int& height)
 
 bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap)
 {
-  const int   flags = 0;
-  FILE* const fp    = input.file;
-
-  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_END)))
-  {
-    DALI_LOG_ERROR("Error seeking to end of file\n");
-    return false;
-  }
+  std::vector<Dali::Devel::PixelBuffer> pixelBuffers;
 
-  long         positionIndicator = ftell(fp);
-  unsigned int jpegBufferSize    = 0u;
-  if(positionIndicator > -1L)
+  bool result = DecodeJpeg(input, pixelBuffers, false);
+  if(!result && pixelBuffers.empty())
   {
-    jpegBufferSize = static_cast<unsigned int>(positionIndicator);
+    bitmap.Reset();
   }
-
-  if(DALI_UNLIKELY(0u == jpegBufferSize))
+  else
   {
-    DALI_LOG_ERROR("Jpeg buffer size error\n");
-    return false;
-  }
-
-  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
-  {
-    DALI_LOG_ERROR("Error seeking to start of file\n");
-    return false;
+    bitmap = pixelBuffers[0];
   }
+  return result;
+}
 
-  Vector<unsigned char> jpegBuffer;
-  try
-  {
-    jpegBuffer.ResizeUninitialized(jpegBufferSize);
-  }
-  catch(...)
-  {
-    DALI_LOG_ERROR("Could not allocate temporary memory to hold JPEG file of size %uMB.\n", jpegBufferSize / 1048576U);
-    return false;
-  }
-  unsigned char* const jpegBufferPtr = jpegBuffer.Begin();
+bool LoadPlanesFromJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers)
+{
+  return DecodeJpeg(input, pixelBuffers, true);
+}
 
-  // Pull the compressed JPEG image bytes out of a file and into memory:
-  if(DALI_UNLIKELY(fread(jpegBufferPtr, 1, jpegBufferSize, fp) != jpegBufferSize))
-  {
-    DALI_LOG_ERROR("Error on image file read.\n");
-    return false;
-  }
+bool DecodeJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers, bool decodeToYuv)
+{
+  Vector<uint8_t> jpegBuffer;
+  unsigned int    jpegBufferSize = 0u;
 
-  if(DALI_UNLIKELY(fseek(fp, 0, SEEK_SET)))
+  if(!LoadJpegFile(input, jpegBuffer, jpegBufferSize))
   {
-    DALI_LOG_ERROR("Error seeking to start of file\n");
+    DALI_LOG_ERROR("LoadJpegFile failed\n");
     return false;
   }
 
   auto jpeg = MakeJpegDecompressor();
-
   if(DALI_UNLIKELY(!jpeg))
   {
     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
     return false;
   }
 
-  auto transform = JpegTransform::NONE;
+  uint8_t* const jpegBufferPtr = jpegBuffer.Begin();
+  auto           transform     = JpegTransform::NONE;
 
   // extract exif data
   auto exifData = MakeExifDataFromData(jpegBufferPtr, jpegBufferSize);
@@ -601,46 +784,16 @@ bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::Pixe
     transform = ConvertExifOrientation(exifData.get());
   }
 
-  std::unique_ptr<Property::Map> exifMap;
-  exifMap.reset(new Property::Map());
-
-  if(DALI_LIKELY(exifData))
-  {
-    for(auto k = 0u; k < EXIF_IFD_COUNT; ++k)
-    {
-      auto content = exifData->ifd[k];
-      for(auto i = 0u; i < content->count; ++i)
-      {
-        auto&&      tag       = content->entries[i];
-        const char* shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast<ExifIfd>(k));
-        if(shortName)
-        {
-          AddExifFieldPropertyMap(*exifMap, *tag, static_cast<ExifIfd>(k));
-        }
-      }
-    }
-  }
-
   // Push jpeg data in memory buffer through TurboJPEG decoder to make a raw pixel array:
   int chrominanceSubsampling = -1;
   int preXformImageWidth = 0, preXformImageHeight = 0;
-
-  // In Ubuntu, the turbojpeg version is not correct. so build error occurs.
-  // Temporarily separate Ubuntu and other profiles.
-#ifndef DALI_PROFILE_UBUNTU
   int jpegColorspace = -1;
+
   if(tjDecompressHeader3(jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling, &jpegColorspace) == -1)
   {
     DALI_LOG_ERROR("%s\n", tjGetErrorStr());
     // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
   }
-#else
-  if(tjDecompressHeader2(jpeg.get(), jpegBufferPtr, jpegBufferSize, &preXformImageWidth, &preXformImageHeight, &chrominanceSubsampling) == -1)
-  {
-    DALI_LOG_ERROR("%s\n", tjGetErrorStr());
-    // Do not set width and height to 0 or return early as this sometimes fails only on determining subsampling type.
-  }
-#endif
 
   if(DALI_UNLIKELY(preXformImageWidth == 0 || preXformImageHeight == 0))
   {
@@ -664,153 +817,108 @@ bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::Pixe
 
   TransformSize(requiredWidth, requiredHeight, input.scalingParameters.scalingMode, input.scalingParameters.samplingMode, transform, scaledPreXformWidth, scaledPreXformHeight, scaledPostXformWidth, scaledPostXformHeight);
 
-  // Colorspace conversion options
-  TJPF          pixelLibJpegType = TJPF_RGB;
-  Pixel::Format pixelFormat      = Pixel::RGB888;
-#ifndef DALI_PROFILE_UBUNTU
-  switch(jpegColorspace)
+  bool result = false;
+
+  // Now we support YUV420 only
+  if(decodeToYuv && chrominanceSubsampling == TJSAMP_420 && transform == JpegTransform::NONE)
   {
-    case TJCS_RGB:
-    // YCbCr is not an absolute colorspace but rather a mathematical transformation of RGB designed solely for storage and transmission.
-    // YCbCr images must be converted to RGB before they can actually be displayed.
-    case TJCS_YCbCr:
-    {
-      pixelLibJpegType = TJPF_RGB;
-      pixelFormat      = Pixel::RGB888;
-      break;
-    }
-    case TJCS_GRAY:
-    {
-      pixelLibJpegType = TJPF_GRAY;
-      pixelFormat      = Pixel::L8;
-      break;
-    }
-    case TJCS_CMYK:
-    case TJCS_YCCK:
-    {
-      pixelLibJpegType = TJPF_CMYK;
-      pixelFormat      = Pixel::RGBA8888;
-      break;
-    }
-    default:
+    unsigned char* planes[3];
+
+    // Allocate buffers for each plane and decompress the jpeg buffer into the buffers
+    for(int i = 0; i < 3; i++)
     {
-      pixelLibJpegType = TJPF_RGB;
-      pixelFormat      = Pixel::RGB888;
-      break;
-    }
-  }
-#endif
-  // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
-  bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat);
+      auto planeSize = tjPlaneSizeYUV(i, scaledPostXformWidth, 0, scaledPostXformHeight, chrominanceSubsampling);
 
-  // set metadata
-  GetImplementation(bitmap).SetMetadata(std::move(exifMap));
+      unsigned char* buffer = static_cast<unsigned char*>(malloc(planeSize));
+      if(!buffer)
+      {
+        DALI_LOG_ERROR("Buffer allocation is failed [%d]\n", planeSize);
+        pixelBuffers.clear();
+        return false;
+      }
 
-  auto bitmapPixelBuffer = bitmap.GetBuffer();
+      int           width, height, planeWidth;
+      Pixel::Format pixelFormat = Pixel::RGB888;
 
-  if(tjDecompress2(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char*>(bitmapPixelBuffer), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags) == -1)
-  {
-    std::string errorString = tjGetErrorStr();
+      if(i == 0)
+      {
+        // luminance plane
+        width       = scaledPostXformWidth;
+        height      = scaledPostXformHeight;
+        planeWidth  = tjPlaneWidth(i, scaledPostXformWidth, chrominanceSubsampling);
+        pixelFormat = Pixel::L8;
+      }
+      else
+      {
+        // chrominance plane
+        width       = tjPlaneWidth(i, scaledPostXformWidth, chrominanceSubsampling);
+        height      = tjPlaneHeight(i, scaledPostXformHeight, chrominanceSubsampling);
+        planeWidth  = width;
+        pixelFormat = (i == 1 ? Pixel::CHROMINANCE_U : Pixel::CHROMINANCE_V);
+      }
 
-    if(DALI_UNLIKELY(IsJpegErrorFatal(errorString)))
-    {
-      DALI_LOG_ERROR("%s\n", errorString.c_str());
-      return false;
+      Internal::Adaptor::PixelBufferPtr internal = Internal::Adaptor::PixelBuffer::New(buffer, planeSize, width, height, planeWidth, pixelFormat);
+      Dali::Devel::PixelBuffer          bitmap   = Devel::PixelBuffer(internal.Get());
+      planes[i]                                  = buffer;
+      pixelBuffers.push_back(bitmap);
     }
-    else
+
+    const int flags = 0;
+
+    int decodeResult = tjDecompressToYUVPlanes(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char**>(&planes), scaledPostXformWidth, nullptr, scaledPostXformHeight, flags);
+    if(decodeResult == -1 && IsJpegDecodingFailed())
     {
-      DALI_LOG_WARNING("%s\n", errorString.c_str());
+      pixelBuffers.clear();
+      return false;
     }
+
+    result = true;
   }
+  else
+  {
+    // Colorspace conversion options
+    TJPF          pixelLibJpegType = TJPF_RGB;
+    Pixel::Format pixelFormat      = Pixel::RGB888;
 
-  const unsigned int bufferWidth  = GetTextureDimension(scaledPreXformWidth);
-  const unsigned int bufferHeight = GetTextureDimension(scaledPreXformHeight);
+    GetJpegPixelFormat(jpegColorspace, pixelLibJpegType, pixelFormat);
 
-  bool result = false;
-  switch(transform)
-  {
-    case JpegTransform::NONE:
-    {
-      result = true;
-      break;
-    }
-    // 3 orientation changes for a camera held perpendicular to the ground or upside-down:
-    case JpegTransform::ROTATE_180:
-    {
-      static auto rotate180Functions = TransformFunctionArray{
-        &Rotate180<1>,
-        &Rotate180<3>,
-        &Rotate180<4>,
-      };
-      result = Transform(rotate180Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    case JpegTransform::ROTATE_270:
-    {
-      static auto rotate270Functions = TransformFunctionArray{
-        &Rotate270<1>,
-        &Rotate270<3>,
-        &Rotate270<4>,
-      };
-      result = Transform(rotate270Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    case JpegTransform::ROTATE_90:
-    {
-      static auto rotate90Functions = TransformFunctionArray{
-        &Rotate90<1>,
-        &Rotate90<3>,
-        &Rotate90<4>,
-      };
-      result = Transform(rotate90Functions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    case JpegTransform::FLIP_VERTICAL:
-    {
-      static auto flipVerticalFunctions = TransformFunctionArray{
-        &FlipVertical<1>,
-        &FlipVertical<3>,
-        &FlipVertical<4>,
-      };
-      result = Transform(flipVerticalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    // Less-common orientation changes, since they don't correspond to a camera's physical orientation:
-    case JpegTransform::FLIP_HORIZONTAL:
-    {
-      static auto flipHorizontalFunctions = TransformFunctionArray{
-        &FlipHorizontal<1>,
-        &FlipHorizontal<3>,
-        &FlipHorizontal<4>,
-      };
-      result = Transform(flipHorizontalFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    case JpegTransform::TRANSPOSE:
-    {
-      static auto transposeFunctions = TransformFunctionArray{
-        &Transpose<1>,
-        &Transpose<3>,
-        &Transpose<4>,
-      };
-      result = Transform(transposeFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
-    }
-    case JpegTransform::TRANSVERSE:
+    // Allocate a bitmap and decompress the jpeg buffer into its pixel buffer:
+    Dali::Devel::PixelBuffer bitmap = Dali::Devel::PixelBuffer::New(scaledPostXformWidth, scaledPostXformHeight, pixelFormat);
+
+    // Set metadata
+    if(DALI_LIKELY(exifData))
     {
-      static auto transverseFunctions = TransformFunctionArray{
-        &Transverse<1>,
-        &Transverse<3>,
-        &Transverse<4>,
-      };
-      result = Transform(transverseFunctions, bitmapPixelBuffer, bufferWidth, bufferHeight, pixelFormat);
-      break;
+      std::unique_ptr<Property::Map> exifMap = std::make_unique<Property::Map>();
+
+      for(auto k = 0u; k < EXIF_IFD_COUNT; ++k)
+      {
+        auto content = exifData->ifd[k];
+        for(auto i = 0u; i < content->count; ++i)
+        {
+          auto&&      tag       = content->entries[i];
+          const char* shortName = exif_tag_get_name_in_ifd(tag->tag, static_cast<ExifIfd>(k));
+          if(shortName)
+          {
+            AddExifFieldPropertyMap(*exifMap, *tag, static_cast<ExifIfd>(k));
+          }
+        }
+      }
+
+      GetImplementation(bitmap).SetMetadata(std::move(exifMap));
     }
-    default:
+
+    auto      bitmapPixelBuffer = bitmap.GetBuffer();
+    const int flags             = 0;
+
+    int decodeResult = tjDecompress2(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<unsigned char*>(bitmapPixelBuffer), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags);
+    if(decodeResult == -1 && IsJpegDecodingFailed())
     {
-      DALI_LOG_ERROR("Unsupported JPEG Orientation transformation: %x.\n", transform);
-      break;
+      return false;
     }
+    pixelBuffers.push_back(bitmap);
+
+    // Transform bitmap
+    result = TransformBitmap(scaledPreXformWidth, scaledPreXformHeight, transform, bitmapPixelBuffer, pixelFormat);
   }
 
   return result;
index 45c99e4..41df859 100644 (file)
@@ -2,7 +2,7 @@
 #define DALI_TIZEN_PLATFORM_LOADER_JPEG_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.
@@ -51,6 +51,16 @@ const unsigned char MAGIC_BYTE_2 = 0xD8;
 bool LoadBitmapFromJpeg(const Dali::ImageLoader::Input& input, Dali::Devel::PixelBuffer& bitmap);
 
 /**
+ * Loads the image planes from an JPEG file.  This function checks the header first
+ * and if it is not a JPEG file, then it returns straight away.
+ * @param[in]  input  Information about the input image (including file pointer)
+ * @param[out] pixelBuffers The buffer list where the each plane will be stored
+ * @return true if file decoded successfully, false otherwise
+ * @note If the image file doesn't support to load planes, this method returns one RGB bitmap image.
+ */
+bool LoadPlanesFromJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::PixelBuffer>& pixelBuffers);
+
+/**
  * Loads the header of a JPEG file and fills in the width and height appropriately.
  * If the width and height are set on entry, it will set the width and height
  * to the closest scaled size (exactly as will be loaded by LoadBitmapFromJpeg with the same
index 93418d3..357df28 100644 (file)
@@ -392,6 +392,13 @@ bool HasChannel(Dali::Pixel::Format pixelFormat, Channel channel)
       DALI_LOG_ERROR("Pixel formats for compressed images are not compatible with simple channels.\n");
       break;
     }
+
+    case Dali::Pixel::CHROMINANCE_U:
+    case Dali::Pixel::CHROMINANCE_V:
+    {
+      DALI_LOG_ERROR("Pixel formats for chrominance are not compatible with simple channels.\n");
+      break;
+    }
   }
 
   return false;