Skip to content

Commit

Permalink
Fix color profile decoding
Browse files Browse the repository at this point in the history
  • Loading branch information
awxkee committed Mar 20, 2024
1 parent 782fb7f commit aa801eb
Show file tree
Hide file tree
Showing 7 changed files with 103 additions and 118 deletions.
26 changes: 15 additions & 11 deletions app/src/main/java/com/awxkee/jxlcoder/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import okio.buffer
import okio.sink
import okio.source
import java.io.ByteArrayInputStream
import java.io.File
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.util.UUID
import kotlin.system.measureTimeMillis

Expand Down Expand Up @@ -137,17 +141,17 @@ class MainActivity : ComponentActivity() {
// imagesArray.add(animated)
// }

// val bufferPng = assets.open("lin.png").source().buffer().readByteArray()
// val bitmap = BitmapFactory.decodeByteArray(bufferPng, 0, bufferPng.size)
// lifecycleScope.launch {
// drawables.add(BitmapDrawable(resources, bitmap))
// }
// val jxlBuffer = JxlCoder.encode(bitmap)
// val animated = JxlCoder.decode(jxlBuffer)
// lifecycleScope.launch {
// drawables.add(BitmapDrawable(resources, animated))
// }
////
val bufferPng = assets.open("pexels_house_wall_p3.jpg").source().buffer().readByteArray()
val bitmap = BitmapFactory.decodeByteArray(bufferPng, 0, bufferPng.size)
lifecycleScope.launch {
drawables.add(BitmapDrawable(resources, bitmap))
}
val jxlBuffer = JxlCoder.encode(bitmap)
val decodedEncoded = JxlCoder.decode(jxlBuffer)
lifecycleScope.launch {
drawables.add(BitmapDrawable(resources, decodedEncoded))
}
//
// val buffer5 = assets.open("elephant.png").source().buffer().readByteArray()
// val jxlBufferPNG = JxlCoder.Convenience.apng2JXL(buffer5, quality = 55)
// val buffer = assets.open("abstract_alpha.png").source().buffer().readByteArray()
Expand Down
2 changes: 1 addition & 1 deletion jxlcoder/src/main/cpp/JniDecoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ jobject decodeSampledImageImpl(JNIEnv *env, std::vector<uint8_t> &imageData, jin
}

Eigen::Matrix3f dstProfile = GamutRgbToXYZ(getRec709Primaries(), getIlluminantD65());
Eigen::Matrix3f conversion = sourceProfile * dstProfile.inverse();
Eigen::Matrix3f conversion = dstProfile.inverse() * sourceProfile;

if (useBitmapFloats) {
coder::GamutAdapter<hwy::float16_t> adapter(reinterpret_cast<hwy::float16_t *>(rgbaPixels.data()), stride,
Expand Down
2 changes: 1 addition & 1 deletion jxlcoder/src/main/cpp/JxlAnimatedDecoderCoordinator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ Java_com_awxkee_jxlcoder_JxlAnimatedImage_getFrameImpl(JNIEnv *env, jobject thiz
}

Eigen::Matrix3f dstProfile = GamutRgbToXYZ(getRec709Primaries(), getIlluminantD65());
Eigen::Matrix3f conversion = sourceProfile.inverse() * dstProfile;
Eigen::Matrix3f conversion = dstProfile.inverse() * sourceProfile;

if (useFloat16) {
coder::GamutAdapter<hwy::float16_t> adapter(reinterpret_cast<hwy::float16_t *>(rgbaPixels.data()), stride,
Expand Down
14 changes: 5 additions & 9 deletions jxlcoder/src/main/cpp/colorspaces/GamutAdapter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,11 +193,7 @@ struct ChromaAdaptation {
}
}

if (gammaCorrection == Rec2020) {
pqR = bt2020GammaCorrection(df32, pqR);
pqG = bt2020GammaCorrection(df32, pqG);
pqB = bt2020GammaCorrection(df32, pqB);
} else if (gammaCorrection == DCIP3) {
if (gammaCorrection == DCIP3) {
pqR = dciP3PQGammaCorrection(df32, pqR);
pqG = dciP3PQGammaCorrection(df32, pqG);
pqB = dciP3PQGammaCorrection(df32, pqB);
Expand All @@ -206,10 +202,10 @@ struct ChromaAdaptation {
pqR = gammaOtf(df32, pqR, gammaEval);
pqG = gammaOtf(df32, pqG, gammaEval);
pqB = gammaOtf(df32, pqB, gammaEval);
} else if (gammaCorrection == Rec709) {
pqR = LinearITUR709ToITUR709(df32, pqR);
pqG = LinearITUR709ToITUR709(df32, pqG);
pqB = LinearITUR709ToITUR709(df32, pqB);
} else if (gammaCorrection == Rec709 || gammaCorrection == Rec2020) {
pqR = Rec709Oetf(df32, pqR);
pqG = Rec709Oetf(df32, pqG);
pqB = Rec709Oetf(df32, pqB);
} else if (gammaCorrection == sRGB) {
pqR = SRGBOetf(df32, pqR);
pqG = SRGBOetf(df32, pqG);
Expand Down
168 changes: 78 additions & 90 deletions jxlcoder/src/main/cpp/colorspaces/eotf-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,6 @@ static const float alphaRec2020 = 1.09929682680944f;
using namespace hwy;
using namespace hwy::HWY_NAMESPACE;

HWY_FAST_MATH_INLINE float bt2020GammaCorrection(float linear) {
if (0 <= betaRec2020 && linear < betaRec2020) {
return 4.5f * linear;
} else if (betaRec2020 <= linear && linear < 1) {
return alphaRec2020 * powf(linear, 0.45f) - (alphaRec2020 - 1.0f);
}
return linear;
}

HWY_FAST_MATH_INLINE float ToLinearPQ(float v, const float sdrReferencePoint) {
float o = v;
v = std::max(0.0f, v);
Expand Down Expand Up @@ -110,47 +101,6 @@ HWY_FAST_MATH_INLINE V ToLinearPQ(const D df, V v, const TFromD<D> sdrReferenceP
return v;
}

template<class D, typename V = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE V bt2020GammaCorrection(const D d, V color) {
const V bt2020 = Set(d, betaRec2020);
const V alpha2020 = Set(d, alphaRec2020);
using T = hwy::HWY_NAMESPACE::TFromD<D>;
const auto cmp = color < bt2020;
const auto fourAndHalf = Set(d, static_cast<T>(4.5f));
const auto branch1 = Mul(color, fourAndHalf);
const V power1 = Set(d, static_cast<T>(0.45f));
const V ones = Set(d, static_cast<T>(1.0f));
const V power2 = Sub(alpha2020, ones);
const auto branch2 =
Sub(Mul(alpha2020, coder::HWY_NAMESPACE::Pow(d, color, power1)), power2);
return IfThenElse(cmp, branch1, branch2);
}

template<class D, typename V = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE V LinearITUR709ToITUR709(const D df, V value) {
const auto minCurve = Set(df, static_cast<TFromD<D>>(0.018f));
const auto minPowValue = Set(df, static_cast<TFromD<D>>(4.5f));
const auto minLane = Mul(value, minPowValue);
const auto subValue = Set(df, static_cast<TFromD<D>>(0.099f));
const auto scalePower = Set(df, static_cast<TFromD<D>>(1.099f));
const auto pwrValue = Set(df, static_cast<TFromD<D>>(0.45f));
const auto maxLane = MulSub(coder::HWY_NAMESPACE::Pow(df, value, pwrValue), scalePower,
subValue);
return IfThenElse(value < minCurve, minLane, maxLane);
}

HWY_FAST_MATH_INLINE float SRGBEotf(float v) {
if (v < 0) {
return 0;
}
if (v <= 0.045f) {
return v / 12.92f;
} else if (v <= 1.f) {
return std::powf((v + 0.055f) / 1.055f, 2.4f);
}
return 1.f;
}

HWY_FAST_MATH_INLINE float Rec709Eotf(float v) {
if (v < 0.f) {
return 0.f;
Expand All @@ -162,28 +112,59 @@ HWY_FAST_MATH_INLINE float Rec709Eotf(float v) {
return 1.f;
}

HWY_FAST_MATH_INLINE float FromLinear709(float linear) {
if (linear < 0.f) {
return 0.f;
HWY_FAST_MATH_INLINE float Rec709Oetf(float linear) {
if (linear < 0.0f) {
return 0.0f;
} else if (linear < 0.018053968510807f) {
return linear * 4.5f;
} else if (linear < 1.f) {
return 1.09929682680944f * std::powf(linear, 0.45f) - 0.09929682680944f;
} else if (linear < 1.0f) {
return 1.09929682680944f * powf(linear, 0.45f) - 0.09929682680944f;
} else {
return 1.0f;
}
}

template<class D, typename V = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE V Rec709Oetf(const D df, V value) {
const auto minCutOff = Set(df, static_cast<TFromD<D>>(0.018053968510807f));
const auto minPowValue = Set(df, static_cast<TFromD<D>>(4.5f));
const auto lo = Mul(value, minPowValue);
const auto zeros = Zero(df);
const auto ones = Set(df, 1.0f);
const auto subValue = Set(df, static_cast<TFromD<D>>(0.09929682680944f));
const auto scalePower = Set(df, static_cast<TFromD<D>>(1.09929682680944f));
const auto pwrValue = Set(df, static_cast<TFromD<D>>(0.45f));
const auto ho = MulSub(coder::HWY_NAMESPACE::Pow(df, value, pwrValue), scalePower, subValue);
auto Lc = IfThenElse(And(value < minCutOff, value >= zeros), lo, zeros);
Lc = IfThenZeroElse(value < zeros, Lc);
Lc = IfThenElse(And(value >= minCutOff, value <= ones), ho, Lc);
Lc = IfThenElse(value > ones, ones, Lc);
return Lc;
}

HWY_FAST_MATH_INLINE float SRGBEotf(float v) {
if (v < 0.0f) {
return 0.0f;
} else if (v < 12.92f * 0.0030412825601275209f) {
return v / 12.92f;
} else if (v < 1.0f) {
return std::powf((v + 0.0550107189475866f) / 1.0550107189475866f, 2.4f);
} else {
return 1.0f;
}
return 1.f;
}

template<class D, HWY_IF_F32_D(D), typename T = TFromD<D>, typename V = VFromD<D>>
HWY_FAST_MATH_INLINE V SRGBEotf(D d, V v) {
const auto highCutOff = Set(d, static_cast<T>(1.0f));
const auto zeros = Zero(d);
const auto lowerValueThreshold = Set(d, T(0.045f));
const auto lowerValueThreshold = Set(d, static_cast<T>(12.92f * 0.0030412825601275209f));
const auto lowValueDivider = Set(d, static_cast<T>(1.0f) / static_cast<T>(12.92f));
const auto lowMask = v <= lowerValueThreshold;
const auto lowValue = Mul(v, lowValueDivider);
const auto powerStatic = Set(d, T(2.4f));
const auto addStatic = Set(d, T(0.055f));
const auto scaleStatic = ApproximateReciprocal(Set(d, T(1.055f)));
const auto addStatic = Set(d, T(0.0550107189475866f));
const auto scaleStatic = ApproximateReciprocal(Set(d, T(1.0550107189475866f)));
const auto highValue = Pow(d, Mul(Add(v, addStatic), scaleStatic), powerStatic);
auto result = IfThenElse(And(lowMask, v >= zeros), lowValue, v);
result = IfThenElse(And(v > lowerValueThreshold, v <= highCutOff), highValue, result);
Expand All @@ -193,41 +174,35 @@ HWY_FAST_MATH_INLINE V SRGBEotf(D d, V v) {
}

HWY_FAST_MATH_INLINE float SRGBOetf(const float linear) {
if (linear <= 0.0031308f) {
return 12.92f * linear;
if (linear < 0.0f) {
return 0.0f;
} else if (linear < 0.0030412825601275209f) {
return linear * 12.92f;
} else if (linear < 1.0f) {
return 1.0550107189475866f * powf(linear, 1.0f / 2.4f) - 0.0550107189475866f;
} else {
return 1.055f * std::powf(linear, 1.0f / 2.4f) - 0.055f;
return 1.0f;
}
}

template<class D, typename V = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE V SRGBOetf(const D df, V v) {
const auto zeros = Zero(df);
const auto highCutOff = Set(df, static_cast<TFromD<D>>(1.0f));
const auto minCutOff = Set(df, static_cast<TFromD<D>>(0.0031308f));
const auto minCutOff = Set(df, static_cast<TFromD<D>>(0.0030412825601275209f));
const auto minPowValue = Set(df, static_cast<TFromD<D>>(12.92f));
const auto lowValue = Mul(v, minPowValue);
const auto subValue = Set(df, static_cast<TFromD<D>>(0.055f));
const auto scalePower = Set(df, static_cast<TFromD<D>>(1.055f));
const auto subValue = Set(df, static_cast<TFromD<D>>(0.0550107189475866f));
const auto scalePower = Set(df, static_cast<TFromD<D>>(1.0550107189475866f));
const auto pwrValue = Set(df, static_cast<TFromD<D>>(1.0f / 2.4f));
const auto highValue = MulSub(coder::HWY_NAMESPACE::Pow(df, v, pwrValue), scalePower,
subValue);

const auto highValue = MulSub(coder::HWY_NAMESPACE::Pow(df, v, pwrValue), scalePower, subValue);
auto result = IfThenElse(And(v <= minCutOff, v >= zeros), lowValue, v);
result = IfThenElse(And(v > minCutOff, v <= highCutOff), highValue, result);
result = IfThenElse(v > highCutOff, highCutOff, result);
result = IfThenElse(v < zeros, zeros, result);
result = IfThenZeroElse(v < zeros, result);
return result;
}

HWY_FAST_MATH_INLINE float LinearITUR709ToITUR709(const float linear) {
if (linear <= 0.018053968510807f) {
return 4.5f * linear;
} else {
return 1.09929682680944f * std::powf(linear, 0.45f) - 0.09929682680944f;
}
}

template<class D, typename V = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE V SMPTE428Eotf(const D df, V value) {
const auto scale = Set(df, static_cast<const TFromD<D>>(1.f / 0.91655527974030934f));
Expand Down Expand Up @@ -273,33 +248,48 @@ HWY_FAST_MATH_INLINE float gammaOtf(float linear, const float gamma) {
}

HWY_FAST_MATH_INLINE float Rec601Oetf(float intensity) {
if (intensity < 0.018f) {
if (intensity < 0) {
return 0.f;
} else if (intensity < 0.018f) {
return intensity * 4.5f;
} else if (intensity <= 1.f) {
return 1.099f * std::powf(intensity, 0.45f) - 0.099f;
} else {
return 1.099 * std::powf(intensity, 0.45f) - 0.099f;
return 1.f;
}
}

template<class D, typename T = Vec<D>, HWY_IF_FLOAT(TFromD<D>)>
HWY_FAST_MATH_INLINE T Rec601Eotf(const D d, T intensity) {
const auto topValue = Set(d, 4.5f * 0.018053968510807f);
const auto fourAnd5 = Set(d, 4.5f);
const auto lowMask = intensity < topValue;
const auto lowValues = Div(intensity, fourAnd5);
const auto lowCutOff = Set(d, 4.5f * 0.018053968510807f);
const auto fourAnd5 = Set(d, 1.f / 4.5f);
const auto lowCutOffMask = intensity < lowCutOff;
const auto tressPassCutOffMask = intensity >= lowCutOff;
const auto zeros = Zero(d);
const auto ones = Set(d, 1.f);
const auto lowValues = Mul(intensity, fourAnd5);

const auto addComp = Set(d, 0.09929682680944f);
const auto div1099 = Set(d, 1.f / 1.09929682680944f);
const auto pwScale = Set(d, 1.0f / 0.45f);

const auto high = coder::HWY_NAMESPACE::Pow(d, Mul(Add(intensity, addComp), div1099), pwScale);
return IfThenElse(lowMask, lowValues, high);
auto Lc = IfThenElseZero(And(lowCutOffMask, intensity >= zeros), lowValues);
Lc = IfThenZeroElse(intensity < zeros, Lc);
Lc = IfThenElse(And(tressPassCutOffMask, intensity <= ones), high, Lc);
Lc = IfThenElse(intensity > ones, ones, Lc);
return Lc;
}

HWY_FAST_MATH_INLINE float Rec601Eotf(float intensity) {
if (intensity < 4.5f * 0.018053968510807f) {
if (intensity < 0) {
return 0.f;
} else if (intensity < 4.5f * 0.018053968510807f) {
return intensity / 4.5f;
} else {
} else if (intensity <= 1.f) {
return std::powf((intensity + 0.099f) / 1.099f, 1.0f / 0.45f);
} else {
return 1.f;
}
}

Expand Down Expand Up @@ -393,8 +383,7 @@ HWY_FAST_MATH_INLINE V Log100Sqrt10Eotf(D d, V v) {
}

HWY_FAST_MATH_INLINE float Log100Sqrt10Oetf(float linear) {
return (linear < 0.00316227766f) ? 0.0f
: 1.0f + std::log10f(std::min(linear, 1.f)) / 2.5f;
return (linear < 0.00316227766f) ? 0.0f : 1.0f + std::log10f(std::min(linear, 1.f)) / 2.5f;
}

HWY_FAST_MATH_INLINE float Iec61966Eotf(float gamma) {
Expand Down Expand Up @@ -445,8 +434,7 @@ HWY_FAST_MATH_INLINE float Bt1361Eotf(float gamma) {
if (gamma < -0.25f) {
return -0.25f;
} else if (gamma < 0.f) {
return std::powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) /
-4.f;
return std::powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.f / 0.45f) / -4.f;
} else if (gamma < 4.5f * 0.018053968510807f) {
return gamma / 4.5f;
} else if (gamma < 1.f) {
Expand Down
3 changes: 1 addition & 2 deletions jxlcoder/src/main/cpp/imagebit/CopyUnaligned.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ HWY_DLLEXPORT void
CopyUnaligned(const uint8_t *HWY_RESTRICT src, const uint32_t srcStride, uint8_t *HWY_RESTRICT dst,
const uint32_t dstStride, const uint32_t width,
const uint32_t height, const uint32_t pixelSize) {
HWY_DYNAMIC_DISPATCH(CopyUnalignedRGBA)(src, srcStride, dst, dstStride, width, height,
pixelSize);
HWY_DYNAMIC_DISPATCH(CopyUnalignedRGBA)(src, srcStride, dst, dstStride, width, height, pixelSize);
}

}
Expand Down
6 changes: 2 additions & 4 deletions jxlcoder/src/main/cpp/interop/JxlDecoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,14 +100,12 @@ bool DecodeJpegXlOneShot(const uint8_t *jxl, size_t size,
// Get the ICC color profile of the pixel data
size_t iccSize;
if (JXL_DEC_SUCCESS !=
JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
&iccSize)) {
JxlDecoderGetICCProfileSize(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,&iccSize)) {
return false;
}
JxlColorEncoding clr;
if (JXL_DEC_SUCCESS ==
JxlDecoderGetColorAsEncodedProfile(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,
&clr)) {
JxlDecoderGetColorAsEncodedProfile(dec.get(), JXL_COLOR_PROFILE_TARGET_DATA,&clr)) {
*colorEncoding = clr;
if (clr.color_space == JXL_COLOR_SPACE_RGB && clr.transfer_function == JXL_TRANSFER_FUNCTION_HLG ||
clr.transfer_function == JXL_TRANSFER_FUNCTION_PQ ||
Expand Down

0 comments on commit aa801eb

Please sign in to comment.