Skip to content

Commit

Permalink
Add preliminary support for loading HDR images
Browse files Browse the repository at this point in the history
The Netflix HDR sample images are loading correctly.
Currently the HDR support is limited to SMPTE ST 2084 (BT.2100 PQ).

It is unclear how the HDR support should work with premultiplied alpha
images.
I filed an issue in the av1-avif repository for clarification on that:
AOMediaCodec/av1-avif#197
  • Loading branch information
0xC0000054 committed Nov 5, 2022
1 parent 2cdb582 commit 21e4fb5
Show file tree
Hide file tree
Showing 14 changed files with 951 additions and 35 deletions.
2 changes: 2 additions & 0 deletions src/common/AvifFormat.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ namespace
{
globals->context = nullptr;
globals->imageHandle = nullptr;
globals->imageHandleNclxProfile = nullptr;
globals->image = nullptr;
globals->imageHandleProfileType = heif_color_profile_type_not_present;
globals->saveOptions.quality = 85;
globals->saveOptions.chromaSubsampling = ChromaSubsampling::Yuv422;
globals->saveOptions.compressionSpeed = CompressionSpeed::Default;
Expand Down
2 changes: 2 additions & 0 deletions src/common/AvifFormat.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ struct Globals
{
heif_context* context;
heif_image_handle* imageHandle;
heif_color_profile_nclx* imageHandleNclxProfile;
heif_image* image;
heif_color_profile_type imageHandleProfileType;

SaveUIOptions saveOptions;
bool libheifInitialized;
Expand Down
2 changes: 1 addition & 1 deletion src/common/AvifFormat.r
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ resource 'PiPL' (ResourceID, plugInName " PiPL", purgeable)
noLABColor
},

EnableInfo { "in (PSHOP_ImageMode, RGBMode, RGB48Mode, GrayScaleMode, Gray16Mode)" },
EnableInfo { "in (PSHOP_ImageMode, RGBMode, RGB48Mode, GrayScaleMode, Gray16Mode, RGB96Mode, Gray32Mode)" },

PlugInMaxSize { 1073741824, 1073741824 },

Expand Down
71 changes: 69 additions & 2 deletions src/common/ColorProfileGeneration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,10 @@ void SetIccProfileFromNclx(FormatRecord* formatRecord, const heif_color_profile_
toneCurve.reset(cmsBuildParametricToneCurve(context.get(), 4, Parameters));
description = L"Grayscale (sRGB TRC)";
}
else if (transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_709_5)
else if (transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_709_5 ||
transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_601_6 ||
transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_2020_2_10bit ||
transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_2020_2_12bit)
{
cmsFloat64Number Parameters[5]
{
Expand All @@ -357,7 +360,26 @@ void SetIccProfileFromNclx(FormatRecord* formatRecord, const heif_color_profile_
};

toneCurve.reset(cmsBuildParametricToneCurve(context.get(), 4, Parameters));
description = L"Grayscale (Rec. 709 TRC)";

switch (primaries)
{
case heif_color_primaries_ITU_R_BT_2020_2_and_2100_0:
description = L"Grayscale (BT. 2020)";
break;
case heif_color_primaries_ITU_R_BT_709_5:
case heif_color_primaries_ITU_R_BT_470_6_System_M:
case heif_color_primaries_ITU_R_BT_470_6_System_B_G:
case heif_color_primaries_ITU_R_BT_601_6:
case heif_color_primaries_SMPTE_240M:
case heif_color_primaries_generic_film:
case heif_color_primaries_SMPTE_ST_428_1:
case heif_color_primaries_SMPTE_RP_431_2:
case heif_color_primaries_SMPTE_EG_432_1:
case heif_color_primaries_EBU_Tech_3213_E:
default:
description = L"Grayscale (Rec. 709 TRC)";
break;
}
}

if (toneCurve && description != nullptr)
Expand Down Expand Up @@ -437,6 +459,51 @@ void SetIccProfileFromNclx(FormatRecord* formatRecord, const heif_color_profile_
description);
}
}
else if (primaries == heif_color_primaries_ITU_R_BT_2020_2_and_2100_0)
{
const cmsCIExyY whitepoint = { 0.3127, 0.3290, 1.0f }; // D65
const cmsCIExyYTRIPLE rgbPrimaries =
{
{ 0.708, 0.292, 1.0 },
{ 0.170, 0.797, 1.0 },
{ 0.131, 0.046, 1.0 }
};

ScopedLcmsToneCurve toneCurve;
const wchar_t* description = nullptr;

if (linear)
{
toneCurve.reset(cmsBuildGamma(context.get(), 1.0));
description = L"BT. 2020 (Linear RGB Profile)";
}
else if (transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_2020_2_10bit ||
transferCharacteristics == heif_transfer_characteristic_ITU_R_BT_2020_2_12bit)
{
// BT. 2020 uses the same transfer curve as Rec. 709.
cmsFloat64Number Parameters[5]
{
1.0 / 0.45,
1.0 / 1.099296826809442,
1.0 - 1 / 1.099296826809442,
1.0 / 4.5,
4.5 * 0.018053968510807,
};

toneCurve.reset(cmsBuildParametricToneCurve(context.get(), 4, Parameters));
description = L"BT. 2020";
}

if (toneCurve && description != nullptr)
{
profile = BuildRGBProfile(
context.get(),
&whitepoint,
&rgbPrimaries,
toneCurve.get(),
description);
}
}

if (profile)
{
Expand Down
99 changes: 99 additions & 0 deletions src/common/ColorTransfer.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* This file is part of avif-format, an AV1 Image (AVIF) file format
* plug-in for Adobe Photoshop(R).
*
* Copyright (c) 2021, 2022 Nicholas Hayes
*
* avif-format is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* avif-format is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with avif-format. If not, see <http://www.gnu.org/licenses/>.
*/

#include "ColorTransfer.h"
#include <stdexcept>

namespace
{
// The sRGB reference viewing environment has a maximum luminance level of 80 nits
// See the 'Screen luminance level' value in the sRGB reference viewing environment table
// https://en.wikipedia.org/wiki/SRGB#Viewing_environment
constexpr float srgbMaxLuminanceLevel = 80.0f;
// PQ (SMPTE ST 2084) has a maximum luminance level of 10000 nits
// https://en.wikipedia.org/wiki/Perceptual_quantizer
constexpr float pqMaxLuminanceLevel = 10000;

inline float LinearToPQ(float normalizedLinearValue)
{
// These constant values are taken from the Perceptual quantizer article on Wikipedia
// https://en.wikipedia.org/wiki/Perceptual_quantizer
constexpr float m1 = 2610.0f / 16384.0f;
constexpr float m2 = 2523.0f / 4096.0f * 128.0f;
constexpr float c1 = 3424.0f / 4096.0f; // c3 - c2 + 1
constexpr float c2 = 2413.0f / 4096.0f * 32.0f;
constexpr float c3 = 2392.0f / 4096.0f * 32.0f;

if (normalizedLinearValue < 0.0f)
{
return 0.0f;
}

const float x = powf(normalizedLinearValue, m1);
const float pq = powf((c1 + c2 * x) / (1.0f + c3 * x), m2);

return pq;
}

inline float PQToLinear(float value)
{
// These constant values are taken from the Perceptual quantizer article on Wikipedia
// https://en.wikipedia.org/wiki/Perceptual_quantizer
constexpr float m1 = 2610.0f / 16384.0f;
constexpr float m2 = 2523.0f / 4096.0f * 128.0f;
constexpr float c1 = 3424.0f / 4096.0f; // c3 - c2 + 1
constexpr float c2 = 2413.0f / 4096.0f * 32.0f;
constexpr float c3 = 2392.0f / 4096.0f * 32.0f;

if (value < 0.0f)
{
return 0.0f;
}

const float x = powf(value, 1.0f / m2);
const float normalizedLinear = powf(std::max(x - c1, 0.0f) / (c2 - c3 * x), 1.0f / m1);

// We have to adjust for the difference in the maximum luminance level between
// PQ and sRGB, otherwise the image is too dark.
return normalizedLinear * (pqMaxLuminanceLevel / srgbMaxLuminanceLevel);
}
}

float TransferCurveToLinear(float value, ColorTransferCurveType curveType)
{
switch (curveType)
{
case ColorTransferCurveType::PQ:
return PQToLinear(value);
default:
throw std::runtime_error("Unsupported color transfer curve type.");
}
}

float LinearToTransferCurve(float value, ColorTransferCurveType curveType)
{
switch (curveType)
{
case ColorTransferCurveType::PQ:
return LinearToPQ(value);
default:
throw std::runtime_error("Unsupported color transfer curve type.");
}
}
36 changes: 36 additions & 0 deletions src/common/ColorTransfer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* This file is part of avif-format, an AV1 Image (AVIF) file format
* plug-in for Adobe Photoshop(R).
*
* Copyright (c) 2021, 2022 Nicholas Hayes
*
* avif-format is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* avif-format is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with avif-format. If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef COLORTRANSFER_H
#define COLORTRANSFER_H

#include <algorithm>
#include <math.h>

enum class ColorTransferCurveType
{
PQ
};

float TransferCurveToLinear(float value, ColorTransferCurveType curveType);

float LinearToTransferCurve(float value, ColorTransferCurveType curveType);

#endif // !COLORTRANSFER_H
Loading

0 comments on commit 21e4fb5

Please sign in to comment.