Aspose.PSD for .NET - CMYK images heavily manipulated on Save

Hi,

I’m running into a problem when Opening/Saving a .psd file with the CMYK color mode.

The problem is that performing these 2 actions generate a file that’s very different from the original one - in some cases, the difference is very noticeable.

Here’s the sample source code:

private static void Main(string[] args)
{
    new Aspose.PSD.License().SetLicense("../../../Aspose.PSD.NET.xml");

    string canyon = "../../../canyon.psd";
    string canyonDest = "../../../canyon-dest.psd";

    string apple = "../../../apple.psd";
    string appleDest = "../../../apple-dest.psd";

    using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(canyon))
    {
        image.Save(canyonDest, true);
    }

    using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(apple))
    {
        image.Save(appleDest, true);
    }
}



Here are 2 examples: the “-dest” files are the ones that were saved by Aspose.PSD, as seen in the sample code above.

canyon.png (280.0 KB)
canyon-dest.png (286.0 KB)
apple.jpg (551.6 KB)
apple-dest.jpg (476.1 KB)

These files were converted to PNG to make it easier to see them.


The technical details are:
  • .NET 6.0
  • Aspose.Imaging version 23.5.1
  • Aspose.PSD version 23.5.1

Here’s the sample Canyon file:
canyon.zip (2.9 MB)

This file should be enough to detect the problem, but I can provide the other file as well (apple.psd, which is too large to upload through here).
Regardless, any CMYK PSD seems to have the same behavior.




Can you please let me know if there’s something I can do on my end to work around this, while still maintaining the CMYK color mode?
Or, as an alternative, is the Aspose team able to solve this problem?


For reference, this situation is somehow similar to the bug “PSDNET-1072”.


Thanks in advance!

@Pushbutton

Please check the following code (Using Aspose.PSD useiccprofileconversion):
string canyon = @“canyon.psd”;
string canyonDest = @“canyon_dest.psd”;
string canyonDestPng = @“canyon_dest.png”;

    var opt = new PsdLoadOptions() { UseIccProfileConversion = true };
    using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(canyon, opt))
    {
        image.Save(canyonDest, true);
        image.Save(canyonDestPng, new PngOptions() { ColorType = PngColorType.TruecolorWithAlpha });
    } 

The export to the PNG has the acceptable for CMYK-RGB Conversion color chaging, but for the saving of PSD File it doesn’t work, so was created a task PSDNET-1583: ICC profile conversion doesn’t work if we save to PSD File. The task has the High priority. Until the issues will be fixied you can use export to the PNG (or other format except PSD) and get the pixels from it.

Hi,

Thanks for your fast reply!

I can confirm that converting to a different file format generates an identical image, however the goal is to maintain the original file (PSD), so my only option is to wait for the fix from PSDNET-1583.

Thank you

@Pushbutton as a temporary workaround you can save the needed Layers to the png, then use LoadArgb32Pixels
method and save this converted pixels using SaveArgb32Pixels to the layer back.

Also, there is one obsolete method in the Color Struct, that can help. Please check this:
RGB to CMYK conversion method
CmykColor[] cmykColors = Color.ToCmykIcc(argbColors);

Hello,

I’m checking in to check on the status of the task PSDNET-1583 - is there any update to it, or any expected release date?

Thank you

@Pushbutton Aspose.PSD product team is working on it. It should be delivered in 23.8

Hello,

I’m checking in again. I believe the fix is not available on 23.8 yet - can you please confirm?

Thanks

@Pushbutton we moved this task to 23.9 because of design issues. It’s in Code Review Status.

Hi,

Thank you for the fix - I can confirm that the code block that has been provided is now working correctly.

Unfortunately, that code block was just a tiny sample with the isolated problem and not the full scope of what we need to achieve, so we’re still running into problems with this.

Specifically, we need to edit/manipulate a few pixels from the PSD images/layers, so we load the pixels, edit them and save them back to the image.
Unfortunately, regardless of which method we use, CMYK images get the exact same color distortion that has been fixed on PSDNET-1583.

Here’s a code sample with a few attempts


    private static void Main(string[] args)
    {
        new Aspose.PSD.License().SetLicense("../../../Aspose.PSD.NET.xml");

        string canyon = "../../../canyon.psd";

        string apple = "../../../apple.psd";

        //Fixed
        LoadAndSave(canyon);
        LoadAndSave(apple);

        //Not working correctly
        SavePixels(canyon);
        SavePixels(apple);

        //Not working correctly
        SavePixelsCMYK(canyon);
        SavePixelsCMYK(apple);

        //Not working correctly
        SavePixelsCMYKAndForceMode(canyon);
        SavePixelsCMYKAndForceMode(apple);

        //Not working correctly
        SavePixelsCMYKObsolete(canyon);
        SavePixelsCMYKObsolete(apple);
    }

    private static void LoadAndSave(string path)
    {
        string newPath = path.Replace(Path.GetExtension(path), " - LoadAndSave" + Path.GetExtension(path));
        using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(path))
        {
            image.Save(newPath, true);
        }
    }

    private static void SavePixels(string path)
    {
        string newPath = path.Replace(Path.GetExtension(path), " - SavePixels" + Path.GetExtension(path));
        using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(path))
        {
            foreach (var frame in image.Layers)
            {
                var pixels = frame.LoadPixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height));
                frame.SavePixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height), pixels);
            }
            image.Save(newPath, true);
        }
    }

    private static void SavePixelsCMYK(string path)
    {
        string newPath = path.Replace(Path.GetExtension(path), " - SavePixelsCMYK" + Path.GetExtension(path));
        using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(path))
        {
            foreach (var frame in image.Layers)
            {
                var pixels = frame.LoadCmyk32Pixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height));
                frame.SaveCmyk32Pixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height), pixels);
            }
            image.Save(newPath, true);
        }
    }

    private static void SavePixelsCMYKAndForceMode(string path)
    {
        string newPath = path.Replace(Path.GetExtension(path), " - SavePixelsCMYKAndForceMode" + Path.GetExtension(path));
        using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(path))
        {
            foreach (var frame in image.Layers)
            {
                var pixels = frame.LoadCmyk32Pixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height));
                frame.SaveCmyk32Pixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height), pixels);
            }
            File.Delete(newPath);
            image.Save(newPath, new Aspose.PSD.ImageOptions.PsdOptions() { ColorMode = Aspose.PSD.FileFormats.Psd.ColorModes.Cmyk });
        }
    }

    private static void SavePixelsCMYKObsolete(string path)
    {
        string newPath = path.Replace(Path.GetExtension(path), " - SavePixelsCMYKObsolete" + Path.GetExtension(path));
        using (Aspose.PSD.FileFormats.Psd.PsdImage image = (Aspose.PSD.FileFormats.Psd.PsdImage)Aspose.PSD.Image.Load(path))
        {
            foreach (var frame in image.Layers)
            {
                var pixels = frame.LoadCmykPixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height));
                frame.SaveCmykPixels(new Aspose.PSD.Rectangle(0, 0, frame.Width, frame.Height), pixels);
            }
            image.Save(newPath, true);
        }
    }

From the methods above, only the “LoadAndSave” is working correctly - this doesn’t do anything to the image, however - it’s just the Load/Save operations.

I’m sharing both images from these tests:
https://drive.google.com/file/d/1zYAbUG9in4lGQzD7MFtdul1DdncowBug/view?usp=sharing

Can you please check the code and let me know if there’s anything that can be done on my end to work around this, or if there’s anything that can be changed on Aspose.PSD?

To clarify, the goal is to persist the file as-is, so we cannot use a workaround that manipulates the image type (such as converting it to PNG).

Thank you

@Pushbutton
I’ll be back with workaround soon. At this moment we have opened the following new ticket(s) in our internal issue tracking system and will deliver their fixes according to the terms mentioned in Free Support Policies.

Issue ID(s): PSDNET-1778

You can obtain Paid Support Services if you need support on a priority basis, along with the direct access to our Paid Support management team.

Hello,

I’m checking in about this issue ID - is there any update on its availability?

Thank you

@Pushbutton I checked the issue tracker. It’s look some related CMYK tasks were implemented. I’ll check if the bug was fixed or improve its priority.

@Pushbutton unfortunately, editing color-loss issues are still presented. I’ve increased priority of task.