Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/doc/imageioapi.rst
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,14 @@ just exist in the OIIO namespace as general utilities. (See

.. doxygenfunction:: OIIO::set_colorspace_rec709_gamma

.. doxygenfunction:: OIIO::is_colorspace_srgb

.. doxygenfunction:: OIIO::get_colorspace_rec709_gamma

.. doxygenfunction:: OIIO::get_colorspace_icc_profile

.. doxygenfunction:: OIIO::get_colorspace_cicp

.. doxygenfunction:: OIIO::equivalent_colorspace

|
Expand Down
65 changes: 65 additions & 0 deletions src/doc/pythonbindings.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4140,6 +4140,71 @@ details.
This function was added in OpenImageIO 3.0.


.. py:method:: is_colorspace_srgb (spec, default_to_srgb = True)

Returns `True` if for the purpose of interop, the metadata of the `spec`
specifies a color space that should be encoded as sRGB.

If `default_to_srgb` is `True`, the color space will be assumed to be
sRGB if no color space was specified in the spec.

Example:

.. code-block:: python

if oiio.is_colorspace_srgb (spec) :
print ("The image is sRGB")

This function was added in OpenImageIO 3.1.


.. py:method:: get_colorspace_rec709_gamma (spec)

If the metadata of the `spec` specifies a color space with Rec709
primaries and gamma transfer function, return the gamma value. If not,
return zero.

Example:

.. code-block:: python

gamma = oiio.get_colorspace_rec709_gamma (spec)

This function was added in OpenImageIO 3.1.


.. py:method:: get_colorspace_icc_profile (spec, from_colorspace = True)

Returns the ICC profile from the metadata of the `spec` as a `bytes`
object, either from an "ICCProfile" attribute or from the color space if
`from_colorspace` is `True`. Returns `None` if not found.

Example:

.. code-block:: python

icc_profile = oiio.get_colorspace_icc_profile (spec)

This function was added in OpenImageIO 3.1.


.. py:method:: get_colorspace_cicp (spec, from_colorspace = True)

Returns the CICP code from the metadata of the `spec` as a list of 4
ints, either from a "CICP" attribute or from the color space if
`from_colorspace` is `True`. Returns `None` if not found.

Example:

.. code-block:: python

cicp = oiio.get_colorspace_cicp (spec)
if cicp:
primaries, transfer, matrix, range = cicp

This function was added in OpenImageIO 3.1.


.. py:method:: equivalent_colorspace (a, b)

Return `True` if the color spaces `a` and `b` are equivalent in the
Expand Down
17 changes: 7 additions & 10 deletions src/dpx.imageio/dpxoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -427,18 +427,15 @@ DPXOutput::prep_subimage(int s, bool allocate)
m_desc = get_image_descriptor();

// transfer function
const ColorConfig& colorconfig = ColorConfig::default_colorconfig();
std::string colorspace = spec_s.get_string_attribute("oiio:ColorSpace", "");
if (colorconfig.equivalent(colorspace, "lin_rec709_scene"))
m_transfer = dpx::kLinear;
else if (colorconfig.equivalent(colorspace, "srgb_rec709_scene"))
const float gamma = get_colorspace_rec709_gamma(spec_s);
if (is_colorspace_srgb(spec_s, false))
m_transfer = dpx::kITUR709;
else if (colorconfig.equivalent(colorspace, "g22_rec709_scene")
|| colorconfig.equivalent(colorspace, "g24_rec709_scene")
|| colorconfig.equivalent(colorspace, "g18_rec709_scene")
|| Strutil::istarts_with(colorspace, "Gamma"))
else if (gamma == 1.0f)
m_transfer = dpx::kLinear;
else if (gamma != 0.0f)
m_transfer = dpx::kUserDefined;
else if (colorconfig.equivalent(colorspace, "KodakLog"))
else if (ColorConfig::default_colorconfig().equivalent(
spec_s.get_string_attribute("oiio:ColorSpace"), "KodakLog"))
m_transfer = dpx::kLogarithmic;
else {
std::string dpxtransfer = spec_s.get_string_attribute("dpx:Transfer",
Expand Down
7 changes: 1 addition & 6 deletions src/heif.imageio/heifoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -275,12 +275,7 @@ HeifOutput::close()
std::unique_ptr<heif_color_profile_nclx,
void (*)(heif_color_profile_nclx*)>
nclx(heif_nclx_color_profile_alloc(), heif_nclx_color_profile_free);
const ColorConfig& colorconfig(ColorConfig::default_colorconfig());
const ParamValue* p = m_spec.find_attribute("CICP",
TypeDesc(TypeDesc::INT, 4));
string_view colorspace = m_spec.get_string_attribute("oiio:ColorSpace");
cspan<int> cicp = (p) ? p->as_cspan<int>()
: colorconfig.get_cicp(colorspace);
cspan<int> cicp = get_colorspace_cicp(m_spec);
if (!cicp.empty()) {
nclx->color_primaries = heif_color_primaries(cicp[0]);
nclx->transfer_characteristics = heif_transfer_characteristics(
Expand Down
37 changes: 37 additions & 0 deletions src/include/OpenImageIO/imageio.h
Original file line number Diff line number Diff line change
Expand Up @@ -4377,6 +4377,39 @@ OIIO_API void set_colorspace(ImageSpec& spec, string_view name);
/// @version 3.0
OIIO_API void set_colorspace_rec709_gamma(ImageSpec& spec, float gamma);

/// Returns true if for the purpose of interop, the metadata of the `spec`
/// specifies a color space that should be encoded as sRGB.
///
/// If `default_to_srgb` is true, the color space will be assumed to be sRGB
/// if no color space was specified in the spec.
///
/// @version 3.1
OIIO_API bool is_colorspace_srgb(const ImageSpec& spec,
bool default_to_srgb = true);

/// If the metadata of the `spec` specifies a color space with Rec709
/// primaries and gamma transfer function, return the gamma value. If not,
/// return zero.
///
/// @version 3.1
OIIO_API float get_colorspace_rec709_gamma(const ImageSpec& spec);

/// Returns the ICC profile from the metadata of the `spec`, either from an
/// "ICCProfile" attribute or from the color space if `from_colorspace` is
/// true. Returns an empty vector if not found.
///
/// @version 3.1
OIIO_API std::vector<uint8_t>
get_colorspace_icc_profile(const ImageSpec& spec, bool from_colorspace = true);

/// Returns the CICP code from the metadata of the `spec`, either from a
/// "CICP" attribute or from the color space if `from_colorspace` is true.
/// Returns a cspan of 4 ints, or an empty span if not found.
///
/// @version 3.1
OIIO_API cspan<int> get_colorspace_cicp(const ImageSpec& spec,
bool from_colorspace = true);


/// Are the two named color spaces equivalent, based on the default color
/// config in effect?
Expand Down Expand Up @@ -4764,13 +4797,17 @@ using v3_1::debugfmt;
using v3_1::declare_imageio_format;
using v3_1::equivalent_colorspace;
using v3_1::errorfmt;
using v3_1::get_colorspace_cicp;
using v3_1::get_colorspace_icc_profile;
using v3_1::get_colorspace_rec709_gamma;
using v3_1::get_extension_map;
using v3_1::get_float_attribute;
using v3_1::get_int_attribute;
using v3_1::get_string_attribute;
using v3_1::getattribute;
using v3_1::geterror;
using v3_1::has_error;
using v3_1::is_colorspace_srgb;
using v3_1::is_imageio_format_name;
using v3_1::log_time;
using v3_1::openimageio_version;
Expand Down
37 changes: 14 additions & 23 deletions src/iv/imageviewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,18 +48,6 @@
#include "ivutils.h"


namespace {

inline bool
IsSpecSrgb(const ImageSpec& spec)
{
return equivalent_colorspace(spec.get_string_attribute("oiio:ColorSpace"),
"srgb_rec709_scene");
}

} // namespace


// clang-format off
static const char *s_file_filters = ""
"Image Files (*.bmp *.cin *.dcm *.dds *.dpx *.fits *.gif *.hdr *.ico *.iff "
Expand Down Expand Up @@ -1265,7 +1253,8 @@ ImageViewer::loadCurrentImage(int subimage, int miplevel)
//std::cerr << "Loading HALF-FLOAT as FLOAT\n";
read_format = TypeDesc::FLOAT;
}
if (IsSpecSrgb(image_spec) && !glwin->is_srgb_capable()) {
if (is_colorspace_srgb(image_spec, false)
&& !glwin->is_srgb_capable()) {
// If the image is in sRGB, but OpenGL can't load sRGB textures then
// we'll need to do the transformation on the CPU after loading the
// image. We (so far) can only do this with UINT8 images, so make
Expand All @@ -1280,7 +1269,8 @@ ImageViewer::loadCurrentImage(int subimage, int miplevel)
read_format = TypeDesc::UINT8;
allow_transforms = true;

if (IsSpecSrgb(image_spec) && !glwin->is_srgb_capable())
if (is_colorspace_srgb(image_spec, false)
&& !glwin->is_srgb_capable())
srgb_transform = true;
}

Expand Down Expand Up @@ -1466,7 +1456,7 @@ ImageViewer::exposureMinusOneTenthStop()
img->exposure(img->exposure() - 0.1);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand All @@ -1485,7 +1475,7 @@ ImageViewer::exposureMinusOneHalfStop()
img->exposure(img->exposure() - 0.5);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand All @@ -1504,7 +1494,7 @@ ImageViewer::exposurePlusOneTenthStop()
img->exposure(img->exposure() + 0.1);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand All @@ -1523,7 +1513,7 @@ ImageViewer::exposurePlusOneHalfStop()
img->exposure(img->exposure() + 0.5);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand All @@ -1543,7 +1533,7 @@ ImageViewer::gammaMinus()
img->gamma(img->gamma() - 0.05);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand All @@ -1562,7 +1552,7 @@ ImageViewer::gammaPlus()
img->gamma(img->gamma() + 0.05);
if (!glwin->is_glsl_capable()) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)current_color_mode(),
current_channel());
displayCurrentImage();
Expand Down Expand Up @@ -1594,8 +1584,9 @@ ImageViewer::viewChannel(int c, COLOR_MODE colormode)
if (!glwin->is_glsl_capable()) {
IvImage* img = cur();
if (img) {
bool srgb_transform = (!glwin->is_srgb_capable()
&& IsSpecSrgb(img->spec()));
bool srgb_transform
= (!glwin->is_srgb_capable()
&& is_colorspace_srgb(img->spec(), false));
img->pixel_transform(srgb_transform, (int)colormode, c);
}
} else {
Expand Down Expand Up @@ -2556,4 +2547,4 @@ ImageViewer::flipVertical()
spec->attribute("Orientation", curr_orientation);
}
displayCurrentImage();
}
}
4 changes: 1 addition & 3 deletions src/iv/ivgl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2198,9 +2198,7 @@ IvGL::typespec_to_opengl(const ImageSpec& spec, int nchannels, GLenum& gltype,
break;
}

bool issrgb
= equivalent_colorspace(spec.get_string_attribute("oiio:ColorSpace"),
"srgb_rec709_scene");
bool issrgb = is_colorspace_srgb(spec, false);

glinternalformat = nchannels;
if (nchannels == 1) {
Expand Down
60 changes: 27 additions & 33 deletions src/jpeg.imageio/jpegoutput.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,8 +312,7 @@ JpgOutput::open(const std::string& name, const ImageSpec& newspec,
}
}

if (equivalent_colorspace(m_spec.get_string_attribute("oiio:ColorSpace"),
"srgb_rec709_scene"))
if (is_colorspace_srgb(m_spec, false))
m_spec.attribute("Exif:ColorSpace", 1);

// Write EXIF info
Expand Down Expand Up @@ -364,37 +363,32 @@ JpgOutput::open(const std::string& name, const ImageSpec& newspec,
m_spec.set_format(TypeDesc::UINT8); // JPG is only 8 bit

// Write ICC profile, if we have anything
if (auto icc_profile_parameter = m_spec.find_attribute(ICC_PROFILE_ATTR)) {
cspan<unsigned char> icc_profile((unsigned char*)
icc_profile_parameter->data(),
icc_profile_parameter->type().size());
if (icc_profile.size() && icc_profile.data()) {
/* Calculate the number of markers we'll need, rounding up of course */
size_t num_markers = icc_profile.size() / MAX_DATA_BYTES_IN_MARKER;
if (num_markers * MAX_DATA_BYTES_IN_MARKER
!= std::size(icc_profile))
num_markers++;
int curr_marker = 1; /* per spec, count starts at 1*/
std::vector<JOCTET> profile(MAX_DATA_BYTES_IN_MARKER
+ ICC_HEADER_SIZE);
size_t icc_profile_length = icc_profile.size();
while (icc_profile_length > 0) {
// length of profile to put in this marker
size_t length = std::min(icc_profile_length,
size_t(MAX_DATA_BYTES_IN_MARKER));
icc_profile_length -= length;
// Write the JPEG marker header (APP2 code and marker length)
strcpy((char*)profile.data(), "ICC_PROFILE"); // NOSONAR
profile[11] = 0;
profile[12] = curr_marker;
profile[13] = (JOCTET)num_markers;
OIIO_ASSERT(profile.size() >= ICC_HEADER_SIZE + length);
spancpy(make_span(profile), ICC_HEADER_SIZE, icc_profile,
length * (curr_marker - 1), length);
jpeg_write_marker(&m_cinfo, JPEG_APP0 + 2, profile.data(),
ICC_HEADER_SIZE + length);
curr_marker++;
}
std::vector<uint8_t> icc_profile = get_colorspace_icc_profile(m_spec);
if (icc_profile.size()) {
/* Calculate the number of markers we'll need, rounding up of course */
size_t num_markers = icc_profile.size() / MAX_DATA_BYTES_IN_MARKER;
if (num_markers * MAX_DATA_BYTES_IN_MARKER != std::size(icc_profile))
num_markers++;
int curr_marker = 1; /* per spec, count starts at 1*/
std::vector<JOCTET> profile(MAX_DATA_BYTES_IN_MARKER + ICC_HEADER_SIZE);
size_t icc_profile_length = icc_profile.size();
while (icc_profile_length > 0) {
// length of profile to put in this marker
size_t length = std::min(icc_profile_length,
size_t(MAX_DATA_BYTES_IN_MARKER));
icc_profile_length -= length;
// Write the JPEG marker header (APP2 code and marker length)
strcpy((char*)profile.data(), "ICC_PROFILE"); // NOSONAR
profile[11] = 0;
profile[12] = curr_marker;
profile[13] = (JOCTET)num_markers;
OIIO_ASSERT(profile.size() >= ICC_HEADER_SIZE + length);
spancpy(make_span(profile), ICC_HEADER_SIZE,
cspan<uint8_t>(icc_profile), length * (curr_marker - 1),
length);
jpeg_write_marker(&m_cinfo, JPEG_APP0 + 2, profile.data(),
ICC_HEADER_SIZE + length);
curr_marker++;
}
}

Expand Down
Loading
Loading