Skip to content

Commit

Permalink
ColorConfig additions (AcademySoftwareFoundation#2248)
Browse files Browse the repository at this point in the history
Background: ColorConfig is our wrapper around some OpenColorIO
functionality, so that apps whose only need for OCIO is for the sake
of OIIO color conversion functions, they can go though this thin
interface instead of directly needing to navigate the more complicated
APIs of OCIO. It also allows us to abstract whether OCIO is actually
available, if it's not it understands just a couple hard-coded color
transformations.

* Add getColoSpaceFamilyByName, which was some OCIO functionality we
  did not expose before.

* Add methods that return a vector of strings containing the list of
  all color spaces, looks, displays, or views for a display.

* Expose all this to Python. And add a simple unit test.

* Make sure one Travis test disables OCIO support, so we know what
  breaks under those circumstances.
  • Loading branch information
lgritz committed Jul 4, 2019
1 parent db9d99b commit 467c526
Show file tree
Hide file tree
Showing 14 changed files with 307 additions and 12 deletions.
8 changes: 4 additions & 4 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,10 @@ matrix:
env: WHICHGCC=6 SANITIZE=address USE_PYTHON=0
if: branch =~ /(master|RB|travis|san)/ OR type = pull_request
# One more, just for the heck of it, turn all SIMD off, and also make
# sure we're falling back on libjpeg, not jpeg-turbo, and don't embed
# the plugins (to make sure that doesn't rust away). I guess this
# should/could be both platforms, but in the interest of making the
# tests go faster, don't bother doing it on OSX.
# sure we're falling back on libjpeg, not jpeg-turbo, no OCIO support,
# and don't embed the plugins (to make sure that doesn't rust away). I
# guess this should/could be both platforms, but in the interest of
# making the tests go faster, don't bother doing it on OSX.
# Only build this case for PRs, direct pushes to master or RB branches,
# or if the branch name includes "simd". Other ordinary work branch
# pushes don't need to run this.
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,8 @@ endif()
if (USE_PYTHON AND NOT BUILD_OIIOUTIL_ONLY)
if (NOT SANITIZE_ON_LINUX)
oiio_add_tests (
python-typedesc python-imagespec python-roi python-deep
python-typedesc python-imagespec
python-roi python-deep python-colorconfig
python-imageinput python-imageoutput
python-imagebuf python-imagebufalgo
IMAGEDIR oiio-images
Expand Down
34 changes: 28 additions & 6 deletions src/include/OpenImageIO/color.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,13 +135,23 @@ class OIIO_API ColorConfig {
/// may be either a color space name or a role.
OIIO::TypeDesc getColorSpaceDataType(string_view name, int* bits) const;

/// Retrieve the full list of known color space names, as a vector
/// of strings.
std::vector<std::string> getColorSpaceNames() const;

/// Get the name of the color space family of the named color space,
/// or NULL if none could be identified.
const char* getColorSpaceFamilyByName(string_view name) const;

/// Get the number of Looks defined in this configuration
int getNumLooks() const;

/// Query the name of the specified Look.
const char* getLookNameByIndex(int index) const;

/// Retrieve the full list of known look names, as a vector of strings.
std::vector<std::string> getLookNames() const;

/// Given the specified input and output ColorSpace, request a handle to
/// a ColorProcessor. It is possible that this will return an empty
/// handle, if the inputColorSpace doesnt exist, the outputColorSpace
Expand Down Expand Up @@ -192,17 +202,29 @@ class OIIO_API ColorConfig {
/// Query the name of the specified display.
const char* getDisplayNameByIndex(int index) const;

/// Get the number of views for a given display defined in this configuration
int getNumViews(string_view display) const;
/// Retrieve the full list of known display names, as a vector of
/// strings.
std::vector<std::string> getDisplayNames() const;

/// Get the name of the default display.
const char* getDefaultDisplayName() const;

/// Get the number of views for a given display defined in this
/// configuration. If the display is empty or not specified, the default
/// display will be used.
int getNumViews(string_view display = "") const;

/// Query the name of the specified view for the specified display
const char* getViewNameByIndex(string_view display, int index) const;

/// Query the name of the default display
const char* getDefaultDisplayName() const;
/// Retrieve the full list of known view names for the display, as a
/// vector of strings. If the display is empty or not specified, the
/// default display will be used.
std::vector<std::string> getViewNames(string_view display = "") const;

/// Query the name of the default view for the specified display
const char* getDefaultViewName(string_view display) const;
/// Query the name of the default view for the specified display. If the
/// display is empty or not specified, the default display will be used.
const char* getDefaultViewName(string_view display = "") const;

/// Construct a processor to transform from the given color space
/// to the color space of the given display and view. You may optionally
Expand Down
67 changes: 67 additions & 0 deletions src/libOpenImageIO/color_ocio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -374,6 +374,34 @@ ColorConfig::getColorSpaceNameByIndex(int index) const



const char*
ColorConfig::getColorSpaceFamilyByName(string_view name) const
{
#ifdef USE_OCIO
if (getImpl()->config_) {
OCIO::ConstColorSpaceRcPtr c = getImpl()->config_->getColorSpace(
name.c_str());
if (c)
return c->getFamily();
}
#endif
return NULL;
}



std::vector<std::string>
ColorConfig::getColorSpaceNames() const
{
std::vector<std::string> result;
result.reserve(getImpl()->colorspaces.size());
for (auto& c : getImpl()->colorspaces)
result.push_back(c.first);
return result;
}



int
ColorConfig::getNumLooks() const
{
Expand All @@ -398,6 +426,17 @@ ColorConfig::getLookNameByIndex(int index) const



std::vector<std::string>
ColorConfig::getLookNames() const
{
std::vector<std::string> result;
for (int i = 0, e = getNumLooks(); i != e; ++i)
result.emplace_back(getLookNameByIndex(i));
return result;
}



const char*
ColorConfig::getColorSpaceNameByRole(string_view role) const
{
Expand Down Expand Up @@ -479,10 +518,23 @@ ColorConfig::getDisplayNameByIndex(int index) const



std::vector<std::string>
ColorConfig::getDisplayNames() const
{
std::vector<std::string> result;
for (int i = 0, e = getNumDisplays(); i != e; ++i)
result.emplace_back(getDisplayNameByIndex(i));
return result;
}



int
ColorConfig::getNumViews(string_view display) const
{
#ifdef USE_OCIO
if (display.empty())
display = getDefaultDisplayName();
if (getImpl()->config_)
return getImpl()->config_->getNumViews(display.c_str());
#endif
Expand All @@ -495,6 +547,8 @@ const char*
ColorConfig::getViewNameByIndex(string_view display, int index) const
{
#ifdef USE_OCIO
if (display.empty())
display = getDefaultDisplayName();
if (getImpl()->config_)
return getImpl()->config_->getView(display.c_str(), index);
#endif
Expand All @@ -503,6 +557,19 @@ ColorConfig::getViewNameByIndex(string_view display, int index) const



std::vector<std::string>
ColorConfig::getViewNames(string_view display) const
{
std::vector<std::string> result;
if (display.empty())
display = getDefaultDisplayName();
for (int i = 0, e = getNumViews(display); i != e; ++i)
result.emplace_back(getViewNameByIndex(display, i));
return result;
}



const char*
ColorConfig::getDefaultDisplayName() const
{
Expand Down
2 changes: 1 addition & 1 deletion src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ set (target_name PyOpenImageIO)
set (python_srcs py_imageinput.cpp py_imageoutput.cpp
py_imagecache.cpp py_imagespec.cpp py_roi.cpp
py_imagebuf.cpp py_imagebufalgo.cpp
py_typedesc.cpp py_paramvalue.cpp py_deepdata.cpp
py_typedesc.cpp py_paramvalue.cpp py_deepdata.cpp py_colorconfig.cpp
py_oiio.cpp)

if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
Expand Down
118 changes: 118 additions & 0 deletions src/python/py_colorconfig.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
Copyright 2015 Larry Gritz and the other authors and contributors.
All Rights Reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the software's owners nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
(This is the Modified BSD License)
*/

#include <utility>

#include "py_oiio.h"
#include <OpenImageIO/color.h>

namespace PyOpenImageIO {


// Declare the OIIO ColorConfig class to Python
void
declare_colorconfig(py::module& m)
{
using namespace pybind11::literals;

py::class_<ColorConfig>(m, "ColorConfig")

.def(py::init<>())
.def(py::init<const std::string&>())
.def("geterror",
[](ColorConfig& self) { return PY_STR(self.geterror()); })

.def("getNumColorSpaces", &ColorConfig::getNumColorSpaces)
.def("getColorSpaceNames", &ColorConfig::getColorSpaceNames)
.def("getColorSpaceNameByIndex", &ColorConfig::getColorSpaceNameByIndex)
.def(
"getColorSpaceNameByRole",
[](const ColorConfig& self, const std::string& role) {
return self.getColorSpaceNameByRole(role);
},
"role"_a)
.def(
"getColorSpaceDataType",
[](const ColorConfig& self, const std::string& name) {
int bits = 0;
TypeDesc type = self.getColorSpaceDataType(name, &bits);
return std::make_pair(type, bits);
},
"name"_a)
.def(
"getColorSpaceFamilyByName",
[](const ColorConfig& self, const std::string& name) {
return self.getColorSpaceFamilyByName(name);
},
"name"_a)

.def("getNumLooks", &ColorConfig::getNumLooks)
.def("getLookNameByIndex", &ColorConfig::getLookNameByIndex)
.def("getLookNames", &ColorConfig::getLookNames)

.def("getNumDisplays", &ColorConfig::getNumDisplays)
.def("getDisplayNameByIndex", &ColorConfig::getDisplayNameByIndex)
.def("getDisplayNames", &ColorConfig::getDisplayNames)
.def("getDefaultDisplayName", &ColorConfig::getDefaultDisplayName)

.def(
"getNumViews",
[](const ColorConfig& self, const std::string& display) {
return self.getNumViews(display);
},
"display"_a = "")
.def(
"getViewNameByIndex",
[](const ColorConfig& self, const std::string& display, int index) {
return self.getViewNameByIndex(display, index);
},
"display"_a = "", "index"_a)
.def(
"getViewNames",
[](const ColorConfig& self, const std::string& display) {
return self.getViewNames(display);
},
"display"_a = "")
.def(
"getDefaultViewName",
[](const ColorConfig& self, const std::string& display) {
return self.getDefaultViewName(display);
},
"display"_a = "")
.def("parseColorSpaceFromString",
[](const ColorConfig& self, const std::string& str) {
return std::string(self.parseColorSpaceFromString(str));
})
.def("configname", &ColorConfig::configname);

m.attr("supportsOpenColorIO") = ColorConfig::supportsOpenColorIO();
}

} // namespace PyOpenImageIO
1 change: 1 addition & 0 deletions src/python/py_oiio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ OIIO_DECLARE_PYMODULE(OIIO_PYMODULE_NAME)
declare_imagespec(m);
declare_roi(m);
declare_deepdata(m);
declare_colorconfig(m);

// Main OIIO I/O classes
declare_imageinput(m);
Expand Down
1 change: 1 addition & 0 deletions src/python/py_oiio.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void declare_imageoutput (py::module& m);
void declare_typedesc (py::module& m);
void declare_roi (py::module& m);
void declare_deepdata (py::module& m);
void declare_colorconfig (py::module& m);
void declare_imagecache (py::module& m);
void declare_imagebuf (py::module& m);
void declare_imagebufalgo (py::module& m);
Expand Down
12 changes: 12 additions & 0 deletions testsuite/python-colorconfig/ref/out-noocio-python27.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
getNumColorSpaces = 6
getColorSpaceNames = ['linear', 'default', 'rgb', 'RGB', 'sRGB', 'Rec709']
getNumLooks = 0
getLookNames = []
getNumDisplays = 0
getDisplayNames = []
getDefaultDisplayName = None
getNumViews = 0
getViewNames = []
getDefaultViewName = None

Done.
12 changes: 12 additions & 0 deletions testsuite/python-colorconfig/ref/out-noocio.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
getNumColorSpaces = 6
getColorSpaceNames = [u'linear', u'default', u'rgb', u'RGB', u'sRGB', u'Rec709']
getNumLooks = 0
getLookNames = []
getNumDisplays = 0
getDisplayNames = []
getDefaultDisplayName = None
getNumViews = 0
getViewNames = []
getDefaultViewName = None

Done.
12 changes: 12 additions & 0 deletions testsuite/python-colorconfig/ref/out-python27.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
getNumColorSpaces = 14
getColorSpaceNames = [u'linear', u'sRGB', u'sRGBf', u'rec709', u'Cineon', u'Gamma1.8', u'Gamma2.2', u'Panalog', u'REDLog', u'ViperLog', u'AlexaV3LogC', u'PLogLin', u'SLog', u'raw']
getNumLooks = 0
getLookNames = []
getNumDisplays = 1
getDisplayNames = [u'default']
getDefaultDisplayName = default
getNumViews = 3
getViewNames = [u'None', u'sRGB', u'rec709']
getDefaultViewName =

Done.
12 changes: 12 additions & 0 deletions testsuite/python-colorconfig/ref/out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
getNumColorSpaces = 14
getColorSpaceNames = ['linear', 'sRGB', 'sRGBf', 'rec709', 'Cineon', 'Gamma1.8', 'Gamma2.2', 'Panalog', 'REDLog', 'ViperLog', 'AlexaV3LogC', 'PLogLin', 'SLog', 'raw']
getNumLooks = 0
getLookNames = []
getNumDisplays = 1
getDisplayNames = ['default']
getDefaultDisplayName = default
getNumViews = 3
getViewNames = ['None', 'sRGB', 'rec709']
getDefaultViewName =

Done.
8 changes: 8 additions & 0 deletions testsuite/python-colorconfig/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/usr/bin/env python

import os

os.environ['OCIO'] = colorconfig_file

command += pythonbin + " src/test_colorconfig.py > out.txt"

Loading

0 comments on commit 467c526

Please sign in to comment.