Recursive AI

Convert PSD to PNG in C#

All oneimage recipes

Add the RecursiveAI.OneImage.Psd package, register PsdFormat in the load options, load the PSD and save it as .png. You get the whole document as Photoshop composited it. To work with the layers, PsdFile.Load returns the layer tree. The program below converts layer_effects.psd, an 800×640 test file from the MIT-licensed psd-tools project.

Terminal
dotnet new console -o PsdToPng && cd PsdToPng
dotnet add package RecursiveAI.OneImage.Psd --version 1.10.4
repo=https://raw.githubusercontent.com/psd-tools/psd-tools/main
curl -L -o layer_effects.psd $repo/tests/psd_files/layer_effects.psd

OneImage needs the .NET 10 SDK. Put this in Program.cs and run dotnet run:

C#
using OneImage;
using OneImage.Psd;
 
// PSD is an add-on format: register it for the load.
var options = new LoadOptions
{
    Configuration = new ImageConfiguration().Add(PsdFormat.Instance),
};
 
Image design = Image.Load("layer_effects.psd", options);
design.Save("layer_effects.png");
 
// JPEG has no transparency: put it on white. Thumbnail makes a web preview.
design.Flatten(new Rgba32(255, 255, 255, 255)).Save("layer_effects.jpg");
Image.Thumbnail(design, 400).Save("preview.webp");
 
foreach (string file in new[] { "layer_effects.png", "layer_effects.jpg",
                                "preview.webp" })
{
    ImageInfo info = Image.Identify(file);
    Console.WriteLine($"{file}: {info.Width}x{info.Height}");
}
Text
layer_effects.png: 800x640
layer_effects.jpg: 800x640
preview.webp: 400x320

When the PSD carries a merged copy of the document, that is what Load returns, so the PNG matches what Photoshop showed. A file without one is drawn from its layers. PSB (large document) files load the same way, and CMYK and Lab documents are converted to RGB.

List and export the layers

C#
using OneImage.Layers;
using OneImage.Psd;
 
using PsdFile psd = PsdFile.Load("layer_effects.psd");
Directory.CreateDirectory("layers");
 
int index = 0;
foreach (Layer layer in psd.Document.Root.Children)   // groups have Children too
{
    index++;
    string size = $"{layer.Bounds.Width}x{layer.Bounds.Height}";
    Console.WriteLine($"{layer.Kind} \"{layer.Name}\" {size}");
    if (layer is PixelLayer pixels)
    {
        // The layer's own pixels, at the layer's size.
        pixels.Pixels.Snapshot().Save($"layers/{index:00}.png");
    }
}
Text
Pixel "Background" 800x640
Pixel "Bevel" 190x47
Pixel "Emboss" 270x47
Pixel "Outer Glow" 399x48
Pixel "Inner Glow" 384x48
Pixel "Inner Shadow" 484x48
Pixel "Color Fill" 481x60
Pixel "Drop Shadow" 465x59
Pixel "Gradient Overlay" 600x60
Pixel "Pattern Overlay" 555x60
Pixel "Stroke" 232x48
Pixel "Satin" 180x48

An exported layer holds its own pixels, without the effects drawn when the document is composited, and Bounds gives its position. To change the document, set a layer’s Visible, Opacity or blend mode, then psd.Document.Render() for a PNG or psd.Save("out.psd") for a Photoshop file.

Limits

  • Text and smart objects keep their stored pixels. They aren’t re-laid out or re-rendered when you edit; they are written back unchanged.
  • Renders follow Photoshop closely, not exactly. Layer effects, blend modes and adjustment layers are drawn by OneImage; the PSD docs list what is modelled and how it compares.
  • Untrusted files are bounded: at most 20,000 layers, 64 levels of groups and 64 MiB per data block by default, checked before allocating.

For PDF and SVG, see Convert a PDF to an image and Convert SVG to PNG.