/*
- * Copyright (c) 2023 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2024 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.
"DALI_ENABLE_DECODE_JPEG_TO_YUV_420",
"",
"DALI_ENABLE_DECODE_JPEG_TO_YUV_440",
- "DALI_ENABLE_DECODE_JPEG_TO_YUV_411"};
+ "DALI_ENABLE_DECODE_JPEG_TO_YUV_411",
+ "DALI_ENABLE_DECODE_JPEG_TO_YUV_441"};
+
+const int CHROMINANCE_SUBSAMPLING_OPTIONS_ENV_COUNT = sizeof(CHROMINANCE_SUBSAMPLING_OPTIONS_ENV) / sizeof(CHROMINANCE_SUBSAMPLING_OPTIONS_ENV[0]);
static bool gSubsamplingFormatTable[TJ_NUMSAMP] = {
false,
{
for(int i = 0; i < TJ_NUMSAMP; i++)
{
- auto valueString = Dali::EnvironmentVariable::GetEnvironmentVariable(CHROMINANCE_SUBSAMPLING_OPTIONS_ENV[i]);
+ const char* envName = DALI_LIKELY(i < CHROMINANCE_SUBSAMPLING_OPTIONS_ENV_COUNT) ? CHROMINANCE_SUBSAMPLING_OPTIONS_ENV[i] : "";
+ auto valueString = Dali::EnvironmentVariable::GetEnvironmentVariable(envName);
gSubsamplingFormatTable[i] = valueString ? std::atoi(valueString) : false;
}
gIsSubsamplingFormatTableInitialized = true;
}
- return gSubsamplingFormatTable[chrominanceSubsampling];
+ if(DALI_UNLIKELY(chrominanceSubsampling >= TJ_NUMSAMP))
+ {
+ DALI_LOG_WARNING("WARNING! Input subsampling value [%d] is bigger than turbojpeg library support [%d]\n", chrominanceSubsampling, TJ_NUMSAMP);
+ }
+
+ return chrominanceSubsampling < TJ_NUMSAMP ? gSubsamplingFormatTable[chrominanceSubsampling] : false;
}
/**
auto planeSize = tjPlaneSizeYUV(i, scaledPostXformWidth, 0, scaledPostXformHeight, chrominanceSubsampling);
uint8_t* buffer = static_cast<uint8_t*>(malloc(planeSize));
- if(!buffer)
+ if(DALI_UNLIKELY(!buffer))
{
DALI_LOG_ERROR("Buffer allocation is failed [%d]\n", planeSize);
pixelBuffers.clear();
uint8_t* cmykBuffer = static_cast<uint8_t*>(malloc(sizeof(uint8_t) * scaledPostXformWidth * scaledPostXformHeight * cmykBytesPerPixel));
+ if(DALI_UNLIKELY(!cmykBuffer))
+ {
+ DALI_LOG_ERROR("cmykBuffer allocation is failed [%zu]\n", sizeof(uint8_t) * scaledPostXformWidth * scaledPostXformHeight * cmykBytesPerPixel);
+ return false;
+ }
+
decodeResult = tjDecompress2(jpeg.get(), jpegBufferPtr, jpegBufferSize, reinterpret_cast<uint8_t*>(cmykBuffer), scaledPreXformWidth, 0, scaledPreXformHeight, pixelLibJpegType, flags);
if(DALI_UNLIKELY(decodeResult == -1 && IsJpegDecodingFailed()))
{
switch(pixelFormat)
{
+ case Pixel::L8:
+ {
+ jpegPixelFormat = TJPF_GRAY;
+ break;
+ }
case Pixel::RGB888:
{
jpegPixelFormat = TJPF_RGB;
// Internal jpeg downscaling is the same as our BOX_X sampling modes so only
// apply it if the application requested one of those:
// (use a switch case here so this code will fail to compile if other modes are added)
- bool downscale = true;
+ bool useTurboJpegScaleFactor = true;
switch(samplingMode)
{
case SamplingMode::BOX:
case SamplingMode::BOX_THEN_LINEAR:
case SamplingMode::DONT_CARE:
{
- downscale = true;
+ useTurboJpegScaleFactor = true;
break;
}
case SamplingMode::NO_FILTER:
case SamplingMode::NEAREST:
case SamplingMode::LINEAR:
{
- downscale = false;
+ useTurboJpegScaleFactor = false;
break;
}
}
- int scaleFactorIndex(0);
- if(downscale)
+ int scaleFactorIndex(-1);
+ int fittedScaledWidth(postXformImageWidth);
+ int fittedScaledHeight(postXformImageHeight);
+ if(useTurboJpegScaleFactor)
{
// Find nearest supported scaling factor (factors are in sequential order, getting smaller)
- for(int i = 1; i < numFactors; ++i)
+ for(int i = 0; i < numFactors; ++i)
{
- bool widthLessRequired = TJSCALED(postXformImageWidth, factors[i]) < requiredWidth;
- bool heightLessRequired = TJSCALED(postXformImageHeight, factors[i]) < requiredHeight;
+ int scaledWidth = TJSCALED(postXformImageWidth, factors[i]);
+ int scaledHeight = TJSCALED(postXformImageHeight, factors[i]);
+ bool widthLessRequired = scaledWidth < requiredWidth;
+ bool heightLessRequired = scaledHeight < requiredHeight;
// If either scaled dimension is smaller than the desired one, we were done at the last iteration
if((fittingMode == FittingMode::SCALE_TO_FILL) && (widthLessRequired || heightLessRequired))
{
break;
}
// This factor stays is within our fitting mode constraint so remember it:
- scaleFactorIndex = i;
+ scaleFactorIndex = i;
+ fittedScaledWidth = scaledWidth;
+ fittedScaledHeight = scaledHeight;
}
}
+ const int maxTextureSize = static_cast<int>(Dali::GetMaxTextureSize());
+
// Regardless of requested size, downscale to avoid exceeding the maximum texture size:
- for(int i = scaleFactorIndex; i < numFactors; ++i)
+ if(fittedScaledWidth >= maxTextureSize ||
+ fittedScaledHeight >= maxTextureSize)
{
- // Continue downscaling to below maximum texture size (if possible)
- scaleFactorIndex = i;
-
- if(TJSCALED(postXformImageWidth, (factors[i])) < static_cast<int>(Dali::GetMaxTextureSize()) &&
- TJSCALED(postXformImageHeight, (factors[i])) < static_cast<int>(Dali::GetMaxTextureSize()))
+ for(int i = scaleFactorIndex + 1; i < numFactors; ++i)
{
- // Current scale-factor downscales to below maximum texture size
- break;
+ // Continue downscaling to below maximum texture size (if possible)
+ scaleFactorIndex = i;
+ fittedScaledWidth = TJSCALED(postXformImageWidth, factors[i]);
+ fittedScaledHeight = TJSCALED(postXformImageHeight, factors[i]);
+
+ if(fittedScaledWidth < maxTextureSize &&
+ fittedScaledHeight < maxTextureSize)
+ {
+ // Current scale-factor downscales to below maximum texture size
+ break;
+ }
}
}
// We have finally chosen the scale-factor, return width/height values
- if(scaleFactorIndex > 0)
+ if(scaleFactorIndex >= 0 && scaleFactorIndex < numFactors)
{
preXformImageWidth = TJSCALED(preXformImageWidth, (factors[scaleFactorIndex]));
preXformImageHeight = TJSCALED(preXformImageHeight, (factors[scaleFactorIndex]));
- postXformImageWidth = TJSCALED(postXformImageWidth, (factors[scaleFactorIndex]));
- postXformImageHeight = TJSCALED(postXformImageHeight, (factors[scaleFactorIndex]));
+ postXformImageWidth = fittedScaledWidth;
+ postXformImageHeight = fittedScaledHeight;
}
}