gimp/plug-ins/file-exr/openexr-wrapper.cc

368 lines
8.5 KiB
C++

#include "config.h"
/* These libgimp includes are not needed here at all, but this is a
* convenient place to make sure the public libgimp headers are
* C++-clean. The C++ compiler will choke on stuff like naming
* a struct member or parameter "private".
*/
#include "libgimp/gimp.h"
#include "libgimp/gimpui.h"
#include "openexr-wrapper.h"
#include <ImfInputFile.h>
#include <ImfChannelList.h>
#include <ImfRgbaFile.h>
#include <ImfRgbaYca.h>
#include <ImfStandardAttributes.h>
#include <string>
using namespace Imf;
using namespace Imf::RgbaYca;
using namespace Imath;
struct _EXRLoader
{
_EXRLoader(const char* filename) :
refcount_(1),
file_(filename),
data_window_(file_.header().dataWindow()),
channels_(file_.header().channels())
{
const Channel* chan;
if (channels_.findChannel("R") ||
channels_.findChannel("G") ||
channels_.findChannel("B"))
{
format_string_ = "RGB";
image_type_ = IMAGE_TYPE_RGB;
if ((chan = channels_.findChannel("R")))
pt_ = chan->type;
else if ((chan = channels_.findChannel("G")))
pt_ = chan->type;
else
pt_ = channels_.findChannel("B")->type;
}
else if (channels_.findChannel("Y") &&
(channels_.findChannel("RY") ||
channels_.findChannel("BY")))
{
format_string_ = "RGB";
image_type_ = IMAGE_TYPE_RGB;
pt_ = channels_.findChannel("Y")->type;
// FIXME: no chroma handling for now.
throw;
}
else if (channels_.findChannel("Y"))
{
format_string_ = "Y";
image_type_ = IMAGE_TYPE_GRAY;
pt_ = channels_.findChannel("Y")->type;
}
else
{
throw;
}
if (channels_.findChannel("A"))
{
format_string_.append("A");
has_alpha_ = true;
}
else
{
has_alpha_ = false;
}
switch (pt_)
{
case UINT:
format_string_.append(" u32");
bpc_ = 4;
break;
case HALF:
format_string_.append(" half");
bpc_ = 2;
break;
case FLOAT:
default:
format_string_.append(" float");
bpc_ = 4;
}
}
int readPixelRow(char* pixels,
int bpp,
int row)
{
const int actual_row = data_window_.min.y + row;
FrameBuffer fb;
// This is necessary because OpenEXR expects the buffer to begin at
// (0, 0). Though it probably results in some unmapped address,
// hopefully OpenEXR will not make use of it. :/
char* base = pixels - (data_window_.min.x * bpp);
switch (image_type_)
{
case IMAGE_TYPE_GRAY:
fb.insert("Y", Slice(pt_, base, bpp, 0, 1, 1, 0.5));
if (hasAlpha())
{
fb.insert("A", Slice(pt_, base + bpc_, bpp, 0, 1, 1, 1.0));
}
break;
case IMAGE_TYPE_RGB:
default:
fb.insert("R", Slice(pt_, base + (bpc_ * 0), bpp, 0, 1, 1, 0.0));
fb.insert("G", Slice(pt_, base + (bpc_ * 1), bpp, 0, 1, 1, 0.0));
fb.insert("B", Slice(pt_, base + (bpc_ * 2), bpp, 0, 1, 1, 0.0));
if (hasAlpha())
{
fb.insert("A", Slice(pt_, base + (bpc_ * 3), bpp, 0, 1, 1, 1.0));
}
}
file_.setFrameBuffer(fb);
file_.readPixels(actual_row);
return 0;
}
int getWidth() const {
return data_window_.max.x - data_window_.min.x + 1;
}
int getHeight() const {
return data_window_.max.y - data_window_.min.y + 1;
}
EXRPrecision getPrecision() const {
EXRPrecision prec;
switch (pt_)
{
case UINT:
prec = PREC_UINT;
break;
case HALF:
prec = PREC_HALF;
break;
case FLOAT:
default:
prec = PREC_FLOAT;
}
return prec;
}
EXRImageType getImageType() const {
return image_type_;
}
int hasAlpha() const {
return has_alpha_ ? 1 : 0;
}
cmsHPROFILE getICCProfile() const {
Chromaticities chromaticities;
float whiteLuminance = 1.0;
cmsHPROFILE profile = NULL;
if (hasChromaticities (file_.header ()))
chromaticities = Imf::chromaticities (file_.header ());
else
return NULL;
if (Imf::hasWhiteLuminance (file_.header ()))
whiteLuminance = Imf::whiteLuminance (file_.header ());
else
return NULL;
#if 0
std::cout << "hasChromaticities: "
<< hasChromaticities (file_.header ())
<< std::endl;
std::cout << "hasWhiteLuminance: "
<< hasWhiteLuminance (file_.header ())
<< std::endl;
std::cout << chromaticities.red << std::endl;
std::cout << chromaticities.green << std::endl;
std::cout << chromaticities.blue << std::endl;
std::cout << chromaticities.white << std::endl;
std::cout << std::endl;
#endif
// TODO: maybe factor this out into libgimpcolor/gimpcolorprofile.h ?
cmsCIExyY whitePoint = { chromaticities.white.x,
chromaticities.white.y,
whiteLuminance };
cmsCIExyYTRIPLE CameraPrimaries = { { chromaticities.red.x,
chromaticities.red.y,
whiteLuminance },
{ chromaticities.green.x,
chromaticities.green.y,
whiteLuminance },
{ chromaticities.blue.x,
chromaticities.blue.y,
whiteLuminance } };
double Parameters[2] = { 1.0, 0.0 };
cmsToneCurve *Gamma[3];
Gamma[0] = Gamma[1] = Gamma[2] = cmsBuildParametricToneCurve(0,
1,
Parameters);
profile = cmsCreateRGBProfile (&whitePoint, &CameraPrimaries, Gamma);
cmsFreeToneCurve (Gamma[0]);
if (profile == NULL) return NULL;
cmsSetProfileVersion (profile, 2.1);
cmsMLU *mlu0 = cmsMLUalloc (NULL, 1);
cmsMLUsetASCII (mlu0, "en", "US", "(GIMP internal)");
cmsMLU *mlu1 = cmsMLUalloc(NULL, 1);
cmsMLUsetASCII (mlu1, "en", "US", "color profile from EXR chromaticities");
cmsMLU *mlu2 = cmsMLUalloc(NULL, 1);
cmsMLUsetASCII (mlu2, "en", "US", "color profile from EXR chromaticities");
cmsWriteTag (profile, cmsSigDeviceMfgDescTag, mlu0);
cmsWriteTag (profile, cmsSigDeviceModelDescTag, mlu1);
cmsWriteTag (profile, cmsSigProfileDescriptionTag, mlu2);
cmsMLUfree (mlu0);
cmsMLUfree (mlu1);
cmsMLUfree (mlu2);
return profile;
}
size_t refcount_;
InputFile file_;
const Box2i data_window_;
const ChannelList& channels_;
PixelType pt_;
int bpc_;
EXRImageType image_type_;
bool has_alpha_;
std::string format_string_;
};
EXRLoader*
exr_loader_new (const char *filename)
{
EXRLoader* file;
// Don't let any exceptions propagate to the C layer.
try
{
file = new EXRLoader(filename);
}
catch (...)
{
file = NULL;
}
return file;
}
EXRLoader*
exr_loader_ref (EXRLoader *loader)
{
++loader->refcount_;
return loader;
}
void
exr_loader_unref (EXRLoader *loader)
{
if (--loader->refcount_ == 0)
{
delete loader;
}
}
int
exr_loader_get_width (EXRLoader *loader)
{
int width;
// Don't let any exceptions propagate to the C layer.
try
{
width = loader->getWidth();
}
catch (...)
{
width = -1;
}
return width;
}
int
exr_loader_get_height (EXRLoader *loader)
{
int height;
// Don't let any exceptions propagate to the C layer.
try
{
height = loader->getHeight();
}
catch (...)
{
height = -1;
}
return height;
}
EXRImageType
exr_loader_get_image_type (EXRLoader *loader)
{
// This does not throw.
return loader->getImageType();
}
EXRPrecision
exr_loader_get_precision (EXRLoader *loader)
{
// This does not throw.
return loader->getPrecision();
}
int
exr_loader_has_alpha (EXRLoader *loader)
{
// This does not throw.
return loader->hasAlpha();
}
cmsHPROFILE
exr_loader_get_icc_profile (EXRLoader *loader)
{
return loader->getICCProfile ();
}
int
exr_loader_read_pixel_row (EXRLoader *loader,
char *pixels,
int bpp,
int row)
{
int retval = -1;
// Don't let any exceptions propagate to the C layer.
try
{
retval = loader->readPixelRow(pixels, bpp, row);
}
catch (...)
{
retval = -1;
}
return retval;
}