Support various yuv formats in jpeg decoder 90/277590/3
authorHeeyong Song <heeyong.song@samsung.com>
Mon, 11 Jul 2022 00:50:30 +0000 (09:50 +0900)
committerHeeyong Song <heeyong.song@samsung.com>
Mon, 11 Jul 2022 07:45:29 +0000 (16:45 +0900)
Change-Id: Ibe013f17fd41e8f501ebcf679593773b074d3948

automated-tests/resources/gallery-small-1-yuv411.jpg [new file with mode: 0644]
automated-tests/resources/gallery-small-1-yuv420.jpg [new file with mode: 0644]
automated-tests/resources/gallery-small-1-yuv422.jpg [new file with mode: 0644]
automated-tests/resources/gallery-small-1-yuv440.jpg [new file with mode: 0644]
automated-tests/resources/gallery-small-1-yuv444.jpg [new file with mode: 0644]
automated-tests/resources/lake_front.jpg [deleted file]
automated-tests/src/dali-adaptor/CMakeLists.txt
automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.cpp [new file with mode: 0644]
automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.h [new file with mode: 0644]
automated-tests/src/dali-adaptor/utc-Dali-ImageLoading.cpp
dali/internal/imaging/common/loader-jpeg-turbo.cpp

diff --git a/automated-tests/resources/gallery-small-1-yuv411.jpg b/automated-tests/resources/gallery-small-1-yuv411.jpg
new file mode 100644 (file)
index 0000000..f53b86b
Binary files /dev/null and b/automated-tests/resources/gallery-small-1-yuv411.jpg differ
diff --git a/automated-tests/resources/gallery-small-1-yuv420.jpg b/automated-tests/resources/gallery-small-1-yuv420.jpg
new file mode 100644 (file)
index 0000000..c1362a6
Binary files /dev/null and b/automated-tests/resources/gallery-small-1-yuv420.jpg differ
diff --git a/automated-tests/resources/gallery-small-1-yuv422.jpg b/automated-tests/resources/gallery-small-1-yuv422.jpg
new file mode 100644 (file)
index 0000000..abb6fa2
Binary files /dev/null and b/automated-tests/resources/gallery-small-1-yuv422.jpg differ
diff --git a/automated-tests/resources/gallery-small-1-yuv440.jpg b/automated-tests/resources/gallery-small-1-yuv440.jpg
new file mode 100644 (file)
index 0000000..8a435e7
Binary files /dev/null and b/automated-tests/resources/gallery-small-1-yuv440.jpg differ
diff --git a/automated-tests/resources/gallery-small-1-yuv444.jpg b/automated-tests/resources/gallery-small-1-yuv444.jpg
new file mode 100644 (file)
index 0000000..cac624f
Binary files /dev/null and b/automated-tests/resources/gallery-small-1-yuv444.jpg differ
diff --git a/automated-tests/resources/lake_front.jpg b/automated-tests/resources/lake_front.jpg
deleted file mode 100644 (file)
index 470a679..0000000
Binary files a/automated-tests/resources/lake_front.jpg and /dev/null differ
index dc7415b..2c1668b 100644 (file)
@@ -45,6 +45,7 @@ LIST(APPEND TC_SOURCES
     dali-test-suite-utils/test-render-controller.cpp
     dali-test-suite-utils/test-trace-call-stack.cpp
     dali-test-suite-utils/adaptor-test-adaptor-impl.cpp
+    dali-test-suite-utils/adaptor-environment-variable.cpp
 )
 
 PKG_CHECK_MODULES(${CAPI_LIB} REQUIRED
diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.cpp b/automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.cpp
new file mode 100644 (file)
index 0000000..4693dd6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ * 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 "adaptor-environment-variable.h"
+
+// EXTERNAL INCLUDE
+#include <map>
+
+namespace Dali
+{
+namespace EnvironmentVariable
+{
+namespace
+{
+std::map<std::string, std::string> gEnvironmentVariables;
+} // namespace
+
+const char* GetEnvironmentVariable(const char* variable)
+{
+  auto value = gEnvironmentVariables.find(variable);
+  if(value != gEnvironmentVariables.end())
+  {
+    return value->second.c_str();
+  }
+  return nullptr;
+}
+
+void SetTestEnvironmentVariable(const char* variable, const char* value)
+{
+  gEnvironmentVariables[variable] = value;
+}
+
+} // namespace EnvironmentVariable
+
+} // namespace Dali
diff --git a/automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.h b/automated-tests/src/dali-adaptor/dali-test-suite-utils/adaptor-environment-variable.h
new file mode 100644 (file)
index 0000000..a955f5f
--- /dev/null
@@ -0,0 +1,36 @@
+#ifndef DALI_ADAPTOR_ENVIRONMENT_VARIABLE_H
+#define DALI_ADAPTOR_ENVIRONMENT_VARIABLE_H
+
+/*
+ * 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.
+ * 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.
+ *
+ */
+#define DALI_ENVIRONMENT_VARIABLE_H
+
+#include <cstddef>
+
+namespace Dali
+{
+namespace EnvironmentVariable
+{
+const char* GetEnvironmentVariable(const char* variable);
+
+void SetTestEnvironmentVariable(const char* variable, const char* value);
+
+} // namespace EnvironmentVariable
+
+} // namespace Dali
+
+#endif // DALI_ADAPTOR_ENVIRONMENT_VARIABLE_H
index 12af8b0..2e4a8b5 100644 (file)
@@ -15,6 +15,7 @@
  *
  */
 
+#include <adaptor-environment-variable.h>
 #include <dali-test-img-utils.h>
 #include <dali-test-suite-utils.h>
 
@@ -34,8 +35,16 @@ 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: 128*128, pixel format: RGB888, YUV411
+const char* IMAGE_128_YUV_411 = TEST_RESOURCE_DIR "/gallery-small-1-yuv411.jpg";
+// resolution: 128*128, pixel format: RGB888, YUV420
+const char* IMAGE_128_YUV_420 = TEST_RESOURCE_DIR "/gallery-small-1-yuv420.jpg";
+// resolution: 128*128, pixel format: RGB888, YUV422
+const char* IMAGE_128_YUV_422 = TEST_RESOURCE_DIR "/gallery-small-1-yuv422.jpg";
+// resolution: 128*128, pixel format: RGB888, YUV440
+const char* IMAGE_128_YUV_440 = TEST_RESOURCE_DIR "/gallery-small-1-yuv440.jpg";
+// resolution: 128*128, pixel format: RGB888, YUV444
+const char* IMAGE_128_YUV_444 = TEST_RESOURCE_DIR "/gallery-small-1-yuv444.jpg";
 
 // resolution: 55*64, pixel format: RGB888
 const char* IMAGE_WIDTH_ODD_EXIF1_RGB = TEST_RESOURCE_DIR "/f-odd-exif-1.jpg";
@@ -424,14 +433,85 @@ int UtcDaliDownloadImageN(void)
 
 int UtcDaliLoadImagePlanesFromFileP(void)
 {
+  EnvironmentVariable::SetTestEnvironmentVariable("DALI_ENABLE_DECODE_JPEG_TO_YUV_444", "1");
+  EnvironmentVariable::SetTestEnvironmentVariable("DALI_ENABLE_DECODE_JPEG_TO_YUV_422", "1");
+  EnvironmentVariable::SetTestEnvironmentVariable("DALI_ENABLE_DECODE_JPEG_TO_YUV_420", "1");
+  EnvironmentVariable::SetTestEnvironmentVariable("DALI_ENABLE_DECODE_JPEG_TO_YUV_440", "1");
+  EnvironmentVariable::SetTestEnvironmentVariable("DALI_ENABLE_DECODE_JPEG_TO_YUV_411", "1");
+
   std::vector<Devel::PixelBuffer> pixelBuffers;
 
-  Dali::LoadImagePlanesFromFile(IMAGE_LARGE_2048_YUV_420, pixelBuffers);
+  // yuv 411 format
+  Dali::LoadImagePlanesFromFile(IMAGE_128_YUV_411, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 3, 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::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetWidth(), 32u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetHeight(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetWidth(), 32u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetHeight(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // yuv 420 format
+  Dali::LoadImagePlanesFromFile(IMAGE_128_YUV_420, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 3, 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::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetWidth(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetHeight(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetWidth(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetHeight(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // yuv 422 format
+  Dali::LoadImagePlanesFromFile(IMAGE_128_YUV_422, 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].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[0].GetHeight(), 128u, TEST_LOCATION);
   DALI_TEST_EQUALS(pixelBuffers[0].GetPixelFormat(), Pixel::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetWidth(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetHeight(), 128u, TEST_LOCATION);
   DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetWidth(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetHeight(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // yuv 440 format
+  Dali::LoadImagePlanesFromFile(IMAGE_128_YUV_440, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 3, 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::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetHeight(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetHeight(), 64u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
+
+  pixelBuffers.clear();
+
+  // yuv 444 format
+  Dali::LoadImagePlanesFromFile(IMAGE_128_YUV_444, pixelBuffers);
+  DALI_TEST_EQUALS(pixelBuffers.size(), 3, 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::L8, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetHeight(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[1].GetPixelFormat(), Pixel::CHROMINANCE_U, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetWidth(), 128u, TEST_LOCATION);
+  DALI_TEST_EQUALS(pixelBuffers[2].GetHeight(), 128u, TEST_LOCATION);
   DALI_TEST_EQUALS(pixelBuffers[2].GetPixelFormat(), Pixel::CHROMINANCE_V, TEST_LOCATION);
 
   pixelBuffers.clear();
@@ -445,13 +525,6 @@ int UtcDaliLoadImagePlanesFromFileP(void)
 
   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;
 }
 
index dc39f24..5af46e6 100644 (file)
@@ -36,6 +36,7 @@
 #include <dali/public-api/object/property-map.h>
 
 // INTERNAL HEADERS
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
 #include <dali/devel-api/adaptor-framework/image-loading.h>
 #include <dali/internal/imaging/common/image-operations.h>
 #include <dali/internal/imaging/common/pixel-buffer-impl.h>
@@ -50,6 +51,18 @@ const unsigned int DECODED_L8       = 1;
 const unsigned int DECODED_RGB888   = 3;
 const unsigned int DECODED_RGBA8888 = 4;
 
+const char* CHROMINANCE_SUBSAMPLING_OPTIONS_ENV[] = {"DALI_ENABLE_DECODE_JPEG_TO_YUV_444",
+                                                     "DALI_ENABLE_DECODE_JPEG_TO_YUV_422",
+                                                     "DALI_ENABLE_DECODE_JPEG_TO_YUV_420",
+                                                     "",
+                                                     "DALI_ENABLE_DECODE_JPEG_TO_YUV_440",
+                                                     "DALI_ENABLE_DECODE_JPEG_TO_YUV_411"};
+
+static bool gSubsamplingFormatTable[TJ_NUMSAMP] = {
+  false,
+};
+static bool gIsSubsamplingFormatTableInitialized = false;
+
 /** Transformations that can be applied to decoded pixels to respect exif orientation
   *  codes in image headers */
 enum class JpegTransform
@@ -74,6 +87,22 @@ struct JpegErrorState
   jmp_buf               jumpBuffer;
 };
 
+static bool IsSubsamplingFormatEnabled(int chrominanceSubsampling)
+{
+  if(!gIsSubsamplingFormatTableInitialized)
+  {
+    for(int i = 0; i < TJ_NUMSAMP; i++)
+    {
+      auto valueString           = Dali::EnvironmentVariable::GetEnvironmentVariable(CHROMINANCE_SUBSAMPLING_OPTIONS_ENV[i]);
+      gSubsamplingFormatTable[i] = valueString ? std::atoi(valueString) : false;
+    }
+
+    gIsSubsamplingFormatTableInitialized = true;
+  }
+
+  return gSubsamplingFormatTable[chrominanceSubsampling];
+}
+
 /**
   * @brief Called by the JPEG library when it hits an error.
   * We jump out of the library so our loader code can return an error.
@@ -819,8 +848,8 @@ bool DecodeJpeg(const Dali::ImageLoader::Input& input, std::vector<Dali::Devel::
 
   bool result = false;
 
-  // Now we support YUV420 only
-  if(decodeToYuv && chrominanceSubsampling == TJSAMP_420 && transform == JpegTransform::NONE)
+  // Check decoding format
+  if(decodeToYuv && IsSubsamplingFormatEnabled(chrominanceSubsampling) && transform == JpegTransform::NONE)
   {
     unsigned char* planes[3];