Index was outside the bounds of the array when saving psb

@Dmitriy.Sorokin

Just want to clarify some things

What to you mean on “the issue was more complex”

  1. Does this mean that there are still issues?

And

The fix should be present in 25.2

  1. Does this mean that the saving of files larger than 2gb is fix and be available in 25.2?
  2. Does the issue on opening of the new generated psb file is also fix and be available on 25.2?
  3. Is the index out of bound issue will be resolved on 25.2?

Thanks

@3stan

  1. Issue with saving file larger than 2Gb is fixed
  2. Opening and saving of PSB files work fine
  3. Index out of bounds is fixed

This provided file after the saving still have the visual artifacts. They can be fixed, but the rendering speed will slow down on 25%.

Release 25.2 is planned on the 20th of February estimately.

@Dmitriy.Sorokin, is there update on the 25.2 release?

@paldueso this update will be in 25.2. Release will be in 2-3 days.

The issues you have found earlier (filed as PSDNET-2288) have been fixed in this update. This message was posted using Bugs notification tool by yaroslav.lisovskyi

@Dmitriy.Sorokin

This issue was verified as fixed; however, a problem persists when saving a 2GB PSB file to Azure Blob Storage. The “Index Out of Bound” error still occurs in our lab environment, where all files are stored in Azure Blob Storage. However, in the local environment, where files are saved to local storage, the fix works correctly. Any suggestions on resolving this?

@3stan could you please additionally check if the old version of Aspose.PSD was not cached and correctly updated. Please check if the version of .NET the same in Azure Blob Storage and locally.
Also, please check if the code locally and in the lab environment differs, also please check if the file the same.

@Dmitriy.Sorokin

> the old version of Aspose.PSD was not cached and correctly updated - No other version exist and already cleared the cache. Also the logs show it is using 25.2 version

> Please check if the version of .NET the same in Azure Blob Storage and locally - This is the same. Although in local we are using Emulator for Blob storage. So the file is stored locally

> Also, please check if the code locally and in the lab environment differs - Same code on both local and lab environment.

> also please check if the file the same. - Yes both are the same.

I tried Adding PSDLoadOption and I’ve noticed that when I set the ReadOnlyMode = true, the file is being saved but it does not write any XMP back.

Here is the full code:

private bool UpdateXmpMetadata(UpdateXmpMetadataMediaAction action)
{
    if (!string.IsNullOrEmpty(action.XmpMetadataMapping))
    {
        PsdImage img = null;

        // First attempt to load the PSD/PSB file
        try
        {
            var loadOptions = new PsdLoadOptions()
            {
                BufferSizeHint = 50 * 1048576,
                ReadOnlyMode = false,
                LoadEffectsResource = false, // Faster loading by skipping layer effects
                UseDiskForLoadEffectsResource = true // Prevents high RAM usage for large files
            };

            using (var inputStream = File.OpenRead(action.Path))
            {
                img = (PsdImage)Image.Load(inputStream, loadOptions);
            }
        }
        catch (ImageLoadException e)
        {
            // Cannot suppport PSD/PSB files with Multichannel or Duotone color mode at this time due to known Aspose bug
            if (e.Message.ToLower().Contains("image") && e.InnerException.Message.ToLower().Contains("color mode") && e.InnerException.Message.ToLower().Contains("compression"))
            {
                throw new MediaEngineException("PSD/PSB files with Multichannel or Duotone color mode are not supported.", false);
            }
        }
        catch (Exception)
        {
            // Something unexpected went wrong while loading
            throw;
        }

        // Since we loaded successfully, process it
        using (img)
        {
            // Cannot suppport PSD/PSB files with Lab color mode which have 16 bits per channel at this time due to known Aspose bug
            if (img.ColorMode == ColorModes.Lab && img.BitsPerChannel == 16)
            {
                throw new MediaEngineException("PSD/PSB files with Lab color mode which have 16 bits per channel are not supported.", false);
            }

            // Cannot suppport PSD/PSB files with CMYK color mode which have less than 4 or more than 5 channels at this time due to known Aspose bug
            if (img.ColorMode == ColorModes.Cmyk && (img.ChannelsCount < 4 || img.ChannelsCount > 5))
            {
                throw new MediaEngineException("PSD/PSB files with CMYK color mode must have 4 or 5 channels.", false);
            }

            // Cannot suppport PSD/PSB files with RGB/Lab color mode which have less than 3 or more than 4 channels at this time due to known Aspose bug
            if ((img.ColorMode == ColorModes.Rgb || img.ColorMode == ColorModes.Lab) && (img.ChannelsCount < 3 || img.ChannelsCount > 4))
            {
                throw new MediaEngineException("PSD/PSB files with RGB/Lab color must have 3 or 4 channels.", false);
            }

            XDocument xmpMapping = XDocument.Parse(action.XmpMetadataMapping);

            // Getting the xmp metadata
            XmpPacketWrapper xmpData = img.XmpData;

            if (xmpData == null)
            {
                // Create xmp metadata if the image doesn't contains any xmp data.
                XmpHeaderPi xmpHeader = new XmpHeaderPi(Guid.NewGuid().ToString());
                XmpTrailerPi xmpTrailer = new XmpTrailerPi(true);
                XmpMeta xmpMeta = new XmpMeta();

                xmpData = new XmpPacketWrapper(xmpHeader, xmpTrailer, xmpMeta);
                img.XmpData = xmpData;
            }

            foreach (var mapping in xmpMapping.Descendants("xmpMapping").Elements("add"))
            {
                var xmpNamespace = mapping.Attribute("namespace").Value;
                var xmpName = mapping.Attribute("name").Value;
                var xmpPrefix = mapping.Attribute("prefix").Value;
                var xmpValue = mapping.Value;

                if (xmpData.ContainsPackage(xmpNamespace))
                {
                    // Update or add XMP property value in existing namespace
                    foreach (var xmpPackage in xmpData.Packages)
                    {
                        if (xmpPackage.NamespaceUri.Trim().ToLower() == xmpNamespace.Trim())
                        {
                            if (xmpPackage.ContainsKey(xmpPackage.Prefix + ":" + xmpName))
                            {
                                xmpPackage[(xmpPackage.Prefix + ":" + xmpName)] = xmpValue;
                            }
                            else
                            {
                                xmpPackage.AddValue(xmpPackage.Prefix + ":" + xmpName, xmpValue);
                            }
                        }
                    }
                }
                else
                {
                    // Create XMP namespace, since it does not yet exist
                    XmpBasicPackage xmp = new XmpBasicPackage(xmpPrefix, xmpNamespace);
                    xmp.AddValue(xmpPrefix + ":" + xmpName, xmpValue);
                    xmpData.AddPackage(xmp);
                }
            }

            img.Save(action.OutputPath);
        }

        return true;
    }

    return false;
}

The Code Above works when it is deployed on local environment, however, different behavior when it is on Lab environment where all files are coming from Azure Blob Storage. But when a file is less than 2GB even with a 1.7GB. All are working even on Lab.

This is the error message we’ve got. Note that this is only happening when the file is not on the local storage and file size is 2gb or larger

Adam.Core.MediaEngines.MediaEngineException: Action UpdateXmpMetadata failed to execute using all engines. The last engine (AsposePsd) failed with error “Index was outside the bounds of the array.”. —> System.IndexOutOfRangeException: Index was outside the bounds of the array. at Aspose.PSD.FileFormats.Psd.Layers.ChannelInformation.(Byte[] , Size , Rectangle ) at …(Dictionary2 , Size , Rectangle ) at ..Process(Rectangle , Int32[] , Point , Point ) at .. (Rectangle ) at .( , IList1 , ) at .(Rectangle , , , Int32 , Int32 , ) at .(Rectangle , , ) at .(Object ) at .(MethodBase , Boolean ) at .() at .(Boolean ) at .(Object ) at .() at .(Object , UInt32 ) at .(Boolean ) at .(Object[] , Type[] , Type[] , Object[] ) at .LoadPartialArgb32Pixels(Rectangle , IPartialArgb32PixelLoader ) at . (Rectangle ) at .( , IList1 , ) at .(Rectangle , , , Int32 , Int32 , ) at . (Rectangle , IPartialArgb32PixelLoader ) at .(Int32 , IColorPalette , , Int32 ) at .(StreamContainer , Int32 , IColorPalette , , Int32 ) at Aspose.PSD.FileFormats.Psd.PsdImage.SaveData(Stream stream) at Aspose.PSD.DataStreamSupporter.Save(Stream stream) at Aspose.PSD.DataStreamSupporter.Save(String filePath) at Adam.Core.MediaEngines.AsposePsdMediaEngine.UpdateXmpMetadata(UpdateXmpMetadataMediaAction action) at Adam.Core.MediaEngines.MediaManager.RunAction(MediaAction action, IMediaEngine engine, Exception& exception) --- End of inner exception stack trace --- at Adam.Core.MediaEngines.MediaManager.LogActionFailure(MediaAction action, IMediaEngine lastFailedEngine, Exception lastException) at Adam.Core.MediaEngines.MediaManager.RunInternal(IEnumerable1 engines, IEnumerable`1 actions) at Adam.Core.Orders.UpdateXmpMetadataOrderTargetAction.OnExecute() at Adam.Core.Orders.OrderRunner.ExecuteRegisteredActions()

@3stan we need additional time to investigate it. What’s version of .NET is used? Possible, Stream/StreamContainer classes behavior is different.

@Dmitriy.Sorokin

We deployed this on a different environment (vNext) and it seems this is the real error when saving PSB files with 2GB.

Exception of type ‘System.OutOfMemoryException’ was thrown. Show

System.OutOfMemoryException: Exception of type ‘System.OutOfMemoryException’ was thrown. at System.IO.MemoryStream.set_Capacity(Int32 value) at System.IO.MemoryStream.EnsureCapacity(Int32 value) at System.IO.MemoryStream.Write(Byte[] buffer, Int32 offset, Int32 count) at ​ .Write(Byte[] , Int32 , Int32 ) at Aspose.PSD.FileFormats.Psd.Layers.ChannelInformation.(StreamContainer , Boolean ) at Aspose.PSD.FileFormats.Psd.Layers.LayerResources.LrXxResource.(StreamContainer , Int32 ) at Aspose.PSD.FileFormats.Psd.Layers.LayerResources.LrXxResource.(Int32 ) at .(Int32 ) at .(Int64 ) at .(StreamContainer , Int32 , IColorPalette , , Int32 ) at Aspose.PSD.FileFormats.Psd.PsdImage.SaveData(Stream stream) at Aspose.PSD.DataStreamSupporter.Save(Stream stream) at Aspose.PSD.DataStreamSupporter.Save(String filePath) at Adam.Core.MediaEngines.AsposePsdMediaEngine.UpdateXmpMetadata(UpdateXmpMetadataMediaAction action) at Adam.Core.MediaEngines.MediaManager.RunAction(MediaAction action, IMediaEngine engine, Exception& exception) at Adam.Core.MediaEngines.MediaManager.RunInternal(IEnumerable1 engines, IEnumerable1 actions) at Adam.Core.Orders.UpdateXmpMetadataOrderTargetAction.OnExecute() at Adam.Core.Orders.OrderRunner.ExecuteRegisteredActions()

Which is why we didn’t encountered this in our local environment stated from my previous post as my local environment have higher specs from the other hosted environment.

Here is the code

private bool UpdateXmpMetadata(UpdateXmpMetadataMediaAction action)
{
    if (!string.IsNullOrEmpty(action.XmpMetadataMapping))
    {
        PsdImage img = null;

        // First attempt to load the PSD/PSB file
        try
        {
            var loadOptions = new PsdLoadOptions()
            {
                BufferSizeHint = 50 * 1048576,
                ReadOnlyMode = false,
                LoadEffectsResource = false, // Faster loading by skipping layer effects
                UseDiskForLoadEffectsResource = true // Prevents high RAM usage for large files
            };

            using (var inputStream = File.OpenRead(action.Path))
            {
                img = (PsdImage)Image.Load(inputStream, loadOptions);
            }
        }
        catch (ImageLoadException e)
        {
            // Cannot suppport PSD/PSB files with Multichannel or Duotone color mode at this time due to known Aspose bug
            if (e.Message.ToLower().Contains("image") && e.InnerException.Message.ToLower().Contains("color mode") && e.InnerException.Message.ToLower().Contains("compression"))
            {
                throw new MediaEngineException("PSD/PSB files with Multichannel or Duotone color mode are not supported.", false);
            }
        }
        catch (Exception)
        {
            // Something unexpected went wrong while loading
            throw;
        }

        // Since we loaded successfully, process it
        using (img)
        {
            // Cannot suppport PSD/PSB files with Lab color mode which have 16 bits per channel at this time due to known Aspose bug
            if (img.ColorMode == ColorModes.Lab && img.BitsPerChannel == 16)
            {
                throw new MediaEngineException("PSD/PSB files with Lab color mode which have 16 bits per channel are not supported.", false);
            }

            // Cannot suppport PSD/PSB files with CMYK color mode which have less than 4 or more than 5 channels at this time due to known Aspose bug
            if (img.ColorMode == ColorModes.Cmyk && (img.ChannelsCount < 4 || img.ChannelsCount > 5))
            {
                throw new MediaEngineException("PSD/PSB files with CMYK color mode must have 4 or 5 channels.", false);
            }

            // Cannot suppport PSD/PSB files with RGB/Lab color mode which have less than 3 or more than 4 channels at this time due to known Aspose bug
            if ((img.ColorMode == ColorModes.Rgb || img.ColorMode == ColorModes.Lab) && (img.ChannelsCount < 3 || img.ChannelsCount > 4))
            {
                throw new MediaEngineException("PSD/PSB files with RGB/Lab color must have 3 or 4 channels.", false);
            }

            XDocument xmpMapping = XDocument.Parse(action.XmpMetadataMapping);

            // Getting the xmp metadata
            XmpPacketWrapper xmpData = img.XmpData;

            if (xmpData == null)
            {
                // Create xmp metadata if the image doesn't contains any xmp data.
                XmpHeaderPi xmpHeader = new XmpHeaderPi(Guid.NewGuid().ToString());
                XmpTrailerPi xmpTrailer = new XmpTrailerPi(true);
                XmpMeta xmpMeta = new XmpMeta();

                xmpData = new XmpPacketWrapper(xmpHeader, xmpTrailer, xmpMeta);
                img.XmpData = xmpData;
            }

            foreach (var mapping in xmpMapping.Descendants("xmpMapping").Elements("add"))
            {
                var xmpNamespace = mapping.Attribute("namespace").Value;
                var xmpName = mapping.Attribute("name").Value;
                var xmpPrefix = mapping.Attribute("prefix").Value;
                var xmpValue = mapping.Value;

                if (xmpData.ContainsPackage(xmpNamespace))
                {
                    // Update or add XMP property value in existing namespace
                    foreach (var xmpPackage in xmpData.Packages)
                    {
                        if (xmpPackage.NamespaceUri.Trim().ToLower() == xmpNamespace.Trim())
                        {
                            if (xmpPackage.ContainsKey(xmpPackage.Prefix + ":" + xmpName))
                            {
                                xmpPackage[(xmpPackage.Prefix + ":" + xmpName)] = xmpValue;
                            }
                            else
                            {
                                xmpPackage.AddValue(xmpPackage.Prefix + ":" + xmpName, xmpValue);
                            }
                        }
                    }
                }
                else
                {
                    // Create XMP namespace, since it does not yet exist
                    XmpBasicPackage xmp = new XmpBasicPackage(xmpPrefix, xmpNamespace);
                    xmp.AddValue(xmpPrefix + ":" + xmpName, xmpValue);
                    xmpData.AddPackage(xmp);
                }
            }

            img.Save(action.OutputPath);
        }

        return true;
    }

    return false;
}

If this would be a environment issue, what would be the minimum specs to prevent this such issue or do you think this is a code base issue on the side of Aspose.PSD?

Additional note on this. The app is hosted in a containerized environment with this specs below

AKS node:
OS version: Windows Server 2022 Datacenter
Kubelet version: v1.31.5

AKS pod specs:
resources:
limits:
memory: 10Gi
requests:
cpu: 800m
memory: 1Gi

@3stan fast answer - Aspose.PSD for .NET has 8Gb or RAM on Test Environment. For JAVA version 12GB of RAM is needed to process your file. Adobe Photoshop requires at least 8Gb free Ram Memory for the correct work, so in most cases, 8GB total is too small memory amount.

I’m not sure if we can optimize RAM performance to 1GB in a short time. Aspose.PSD has logic to process images partially in case of insufficient memory, but the files you work with quite large. Aspose.PSD Team works on optimization of RAM consumption, but ETA is unknown for us at this moment.

Can I ask some questions that can help us to satisfy your requirements:

  1. Is the time of processing is important for your project?
  2. Is it suitable for your project if for the temporary storage of Pixel data will be used file system?

@Dmitriy.Sorokin

To answer your question

    1. Is the time of processing is important for your project? - I guess this is ok since the processing of image is done via background service
    1. Is it suitable for your project if for the temporary storage of Pixel data will be used file system? - If this is possible yes it is not a problem as long as the it does not affect the original file

Also, I’ve encountered this before on PSB but just to confirm, Is there a limit size on loading of PSB file? if yes then what is the limit.

Can you walk me through on a high level details on the background process how the PSB is saved with XMP writeback. We just want to have an idea on how much more memory we need to upscale our environment in order to process this file size.

Additional Note that 856mb of file can be process on our current environment without any problem and assuming because it is less than our current memory set (1GB)

@3stan could you please clarify? Do you need only change XMP data without editing the Image? There are 2 main ways of how Aspose.PSD processes the files. The default is Edit-mode, when Aspose.PSD rerender changes and some non-changed Layers, and the second one - Read Only Mode. If you don’t need rerender, maybe improvement of Read Only Mode can be useful for you? Or maybe the additional option like prohibition of rerender can help?

@Dmitriy.Sorokin

Yes. The code I posted here is it only updates the XMP for the PSB file. There will no other changes on the files layers. You mean the Read-Only mode in PSDLoadOption? If this is the one, yes I’ve tried this option yet, it does not write the XMP.

@3stan
I’ve created task: Implement for PSD Load Options Parameter that ignores Pixel Data (doesn’t read it if possible) and gives ability to update metadata without rerendering. I think this feature can be useful for these cases.

Issue ID(s): PSDNET-2382

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.

@Dmitriy.Sorokin

Thanks you for your response. Is this a continuation of the past issue on ticket or a new one? If ever, when to we expect this be included on a release?

Thanks

@3stan at this moment I have no ETA, the task is quite complex. But the team is excited about the task. So, we will try to make it before June.