Multilayered PSD files
Posted: 2016-01-09T15:01:35-07:00
I am trying to create a multilayered PSD file from a multi part OpenEXR image. My code works fine when I save the image as a multilayer tiff or when I save a single layer in a PSD file. However, when I save a multilayered PSD file, all channels appear shifted or broken as if the stride was wrong. I believe this is a bug with ImageMagick (6.9.2-10) or with my usage of the api (more likely).
I post the relevant part of my source code in case someone spots an obvious problem (if someone has working code, it would be appreciated):
I post the relevant part of my source code in case someone spots an obvious problem (if someone has working code, it would be appreciated):
Code: Select all
CMedia* p = const_cast< CMedia* >( this );
const char* old_channel = channel();
stringArray::const_iterator i;
stringArray::const_iterator e;
if ( opts->all_layers() )
{
// Handle multiple layers
i = p->layers().begin();
e = p->layers().end();
}
else
{
// Handle a single layer
i = p->layers().begin();
e = p->layers().end();
for ( ; i != e; ++i )
{
if ( ( old_channel && *i == old_channel ) || *i == _("Color") )
{
e = i+1;
break;
}
}
if ( i == e )
{
i = p->layers().begin();
e = i+1;
}
}
Buffers bufs;
std::string root = "ZXVCW#!";
for ( ; i != e; ++i )
{
std::string x = *i;
// std::cerr << "layer " << x << std::endl;
if ( x == _("Lumma") || x == _("Alpha Overlay") ||
x == _("Red") || x == _("Green") ||
x == _("Blue") || x == _("Alpha") ||
x == N_("RY") || x == N_("BY") ||
x.find( _("anaglyph") ) != std::string::npos ||
x.find( _("stereo") ) != std::string::npos )
{
continue;
}
std::string ext = x;
size_t pos = ext.rfind( '.' );
if ( pos != std::string::npos && pos != ext.size() )
{
ext = ext.substr( pos+1, ext.size() );
}
std::transform( ext.begin(), ext.end(), ext.begin(),
(int(*)(int)) toupper);
if ( x.find(root) == 0 && root != "Z" ) continue;
root = x;
// This is the root layer
if ( x == _("Color") ) x = "";
p->channel( x.c_str() );
mrv::image_type_ptr pic = hires();
mrv::Recti daw = data_window();
image_type::Format format = pic->format();
bool has_alpha = pic->has_alpha();
bool must_convert = false;
const char* channels;
switch ( format )
{
case image_type::kRGB:
channels = N_("RGB"); break;
case image_type::kRGBA:
channels = N_("RGBA"); break;
case image_type::kBGRA:
channels = N_("BGRA"); break;
case image_type::kBGR:
channels = N_("BGR"); break;
case image_type::kLumma:
channels = N_("I"); break;
case image_type::kLummaA:
channels = N_("IA"); break;
default:
must_convert = true;
channels = N_("RGB");
if ( has_alpha ) channels = N_("RGBA");
break;
}
// std::cerr << "imagemagick channels " << channels
// << " pic->channels " << pic->channels() << " alpha? "
// << has_alpha << std::endl;
StorageType storage = CharPixel;
switch( pic->pixel_type() )
{
case image_type::kShort:
storage = ShortPixel;
break;
case image_type::kInt:
storage = IntegerPixel;
break;
case image_type::kFloat:
storage = FloatPixel;
break;
case image_type::kHalf:
storage = ShortPixel;
must_convert = true;
break;
case image_type::kByte:
default:
storage = CharPixel;
break;
}
if ( o->pixel_type() != storage )
{
LOG_INFO( _("Original pixel type is ")
<< pixel_storage( storage )
<< (". Saving pixel type is ")
<< pixel_storage( o->pixel_type() )
<< "." );
must_convert = true;
}
// if ( gamma() != 1.0 )
// must_convert = true;
if ( opts->opengl() )
must_convert = false;
// Set matte (alpha)
// MagickBooleanType matte = MagickFalse;
// if ( has_alpha ) matte = MagickTrue;
// MagickSetImageMatte( wand, matte );
/**
* Load image onto wand
*
*/
boost::uint8_t* pixels = NULL;
if ( must_convert )
{
unsigned pixel_size = 1;
switch( o->pixel_type() )
{
case ShortPixel:
pixel_size = sizeof(short);
break;
case IntegerPixel:
pixel_size = sizeof(int);
break;
case FloatPixel:
pixel_size = sizeof(float);
break;
case DoublePixel:
pixel_size = sizeof(double);
break;
default:
case CharPixel:
pixel_size = sizeof(char);
break;
}
unsigned data_size = width()*height()*pic->channels()*pixel_size;
pixels = new boost::uint8_t[ data_size ];
bufs.push_back( pixels );
}
else
{
pixels = (boost::uint8_t*)pic->data().get();
}
unsigned dw = pic->width();
unsigned dh = pic->height();
MagickWand* w = NewMagickWand();
status = MagickConstituteImage( w, dw, dh, channels,
o->pixel_type(), pixels );
if (status == MagickFalse)
{
destroyPixels(bufs);
ThrowWandException( wand );
}
if ( !must_convert )
{
if ( pic->frame() == first_frame() )
{
LOG_INFO( _("No conversion needed. Gamma: ") << _gamma );
}
MagickSetImageGamma( wand, _gamma );
}
else
{
if ( pic->frame() == first_frame() )
{
LOG_INFO( _("Conversion needed. Gamma: 1.0") );
}
MagickSetImageGamma( wand, 1.0 );
}
if ( must_convert )
{
double one_gamma = 1.0 / _gamma;
for ( unsigned y = 0; y < dh; ++y )
{
for ( unsigned x = 0; x < dw; ++x )
{
//Note: ImagePixel is always float regardless of what pic stores it as
ImagePixel p = pic->pixel( x, y );
if ( p.r > 0.f && isfinite(p.r) )
p.r = pow( p.r, one_gamma );
if ( p.g > 0.f && isfinite(p.g) )
p.g = pow( p.g, one_gamma );
if ( p.b > 0.f && isfinite(p.b) )
p.b = pow( p.b, one_gamma );
status = MagickImportImagePixels(w, x, y, 1, 1, channels,
FloatPixel, &p[0] );
if (status == MagickFalse)
{
ThrowWandException( wand );
}
}
}
if (status == MagickFalse)
{
destroyPixels(bufs);
ThrowWandException( wand );
}
}
if ( has_alpha )
{
status = MagickSetImageAlphaChannel( w,
ActivateAlphaChannel );
if ( status == MagickFalse )
{
ThrowWandException( wand );
}
}
MagickSetLastIterator( wand );
MagickSetImageCompression( wand, compression );
MagickSetImageCompression( w, compression );
std::string label = x;
if ( label[0] == '#' )
{
pos = label.find( ' ' );
if ( pos != std::string::npos && pos != label.size() )
{
label = label.substr( pos+1, label.size() );
}
}
if ( label == "" )
{
MagickSetImageProperty( w, "label", NULL );
// This is the Color channel, Add it as first channel
MagickSetFirstIterator( wand );
}
else
{
MagickSetImageProperty( w, "label", label.c_str() );
}
#if 1
// Handle OpenEXR/PSD offsets
Image* img = GetImageFromMagickWand( w );
img->page.x = daw.x();
img->page.y = daw.y();
img->page.width = daw.w();
img->page.height = daw.h();
#endif
MagickAddImage( wand, w );
DestroyMagickWand( w );
}
//
// Store EXIF and IPTC data (if any)
//
/**
* Write out image layer(s)
*
*/
status = MagickWriteImages( wand, file, MagickTrue );
if ( status == MagickFalse )
ThrowWandException( wand );
destroyPixels(bufs);
DestroyMagickWand( wand );
if (status == MagickFalse)
{
ThrowWandException( wand );
}
p->channel( old_channel );
}