Beispiel #1
0
        static int Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage: PSD2PAM.exe FILE.psd FILE.pam");
                return(1);
            }

            // read the PSD file
            var photochop = new PSDFile();

            using (var reading = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                photochop.Read(reading);

                var versionInfo = photochop.ImageResources.FirstOrDefault(ir => ir.ID == 0x0421);
                if (versionInfo?.Data[4] == 0)
                {
                    // does not have valid precomposed image
                    Console.WriteLine("PSD2PAM requires that a valid precomposed image is part of the file.");
                    return(1);
                }

                int width        = photochop.Width;
                int height       = photochop.Height;
                int channelCount = photochop.NumberOfChannels;
                int depth        = photochop.Depth;
                if (depth == 1)
                {
                    Console.WriteLine("PSD2PAM doesn't currently support 1-bit images.");
                    return(1);
                }

                long maxValue;
                switch (depth)
                {
                case 8:
                    maxValue = 0xFF;
                    break;

                case 16:
                    maxValue = 0xFFFF;
                    break;

                case 32:
                    maxValue = 0xFFFFFFFF;
                    break;

                default:
                    Console.WriteLine("Unsupported depth {0}.", depth);
                    return(1);
                }

                int bytesPerColorComponent = depth / 8;

                string tupleTypeString = "";
                switch (photochop.ColorMode)
                {
                case ColorMode.RGB:
                    tupleTypeString = "TUPLTYPE RGB\n";
                    break;

                case ColorMode.CMYK:
                    tupleTypeString = "TUPLTYPE CMYK\n";
                    break;

                case ColorMode.Grayscale:
                    tupleTypeString = "TUPLTYPE GRAYSCALE\n";
                    break;
                }

                string header = string.Format(
                    CultureInfo.InvariantCulture,
                    "P7\nWIDTH {0}\nHEIGHT {1}\nDEPTH {2}\nMAXVAL {3}\n{4}ENDHDR\n",
                    width,
                    height,
                    channelCount,
                    maxValue,
                    tupleTypeString
                    );

                reading.Seek(photochop.PrecomposedImageData.Offset, SeekOrigin.Begin);

                using (var tempFiles = new TemporaryFilesDeleteOnDispose(1 + channelCount))
                {
                    // get planar image
                    using (var planarImageStorage = new FileStream(tempFiles.FileNames[0],
                                                                   FileMode.Create, FileAccess.Write, FileShare.None))
                        using (var zippage = new GZipStream(planarImageStorage, CompressionLevel.Fastest))
                        {
                            switch (photochop.PrecomposedImageData.Compression)
                            {
                            case CompressionType.RawData:
                                PixelDataDecoding.DecodeRawData(reading, zippage, null);
                                break;

                            case CompressionType.PackBits:
                                int scanlineCount = height * channelCount;
                                PixelDataDecoding.DecodePackBits(reading, zippage, scanlineCount, photochop.Version == 2);
                                break;

                            case CompressionType.ZipWithoutPrediction:
                                PixelDataDecoding.DecodeZip(reading, zippage, null);
                                break;

                            case CompressionType.ZipWithPrediction:
                                PixelDataDecoding.DecodeZipPredicted(reading, zippage, null, depth, width);
                                break;
                            }
                        }

                    // split planar image into plane files
                    var buf = new byte[width * bytesPerColorComponent];
                    using (var reader = new FileStream(tempFiles.FileNames[0], FileMode.Open, FileAccess.Read,
                                                       FileShare.Read))
                        using (var unzippage = new GZipStream(reader, CompressionMode.Decompress))
                        {
                            for (int c = 0; c < channelCount; ++c)
                            {
                                using (var writer = new FileStream(tempFiles.FileNames[1 + c], FileMode.Create,
                                                                   FileAccess.Write, FileShare.None))
                                    using (var zippage = new GZipStream(writer, CompressionLevel.Fastest))
                                    {
                                        for (int y = 0; y < height; ++y)
                                        {
                                            if (y % 32 == 31)
                                            {
                                                Console.WriteLine("splitting channel {0}/{1} row {2}/{3}", c + 1, channelCount,
                                                                  y + 1, height);
                                            }

                                            int rd = unzippage.Read(buf, 0, buf.Length);
                                            if (rd < buf.Length)
                                            {
                                                throw new Exception("short read");
                                            }
                                            zippage.Write(buf, 0, buf.Length);
                                        }
                                    }
                            }
                        }

                    using (var streamDisposal = new DisposableStreamGroup())
                    {
                        // reopen the planes
                        for (int c = 0; c < channelCount; ++c)
                        {
                            var stream = new FileStream(tempFiles.FileNames[1 + c], FileMode.Open,
                                                        FileAccess.Read, FileShare.Read);
                            var unzipper = new GZipStream(stream, CompressionMode.Decompress, leaveOpen: false);
                            streamDisposal.Streams.Add(unzipper);
                        }

                        // interleave the components
                        var pixel = new byte[bytesPerColorComponent];
                        using (var writer = new FileStream(args[1], FileMode.Create, FileAccess.Write))
                            using (var zipper = new GZipStream(writer, CompressionLevel.Fastest))
                                using (var bufferer = new BufferedStream(zipper, 8 * 1024 * 1024))
                                {
                                    // write PAM header
                                    var pamHeaderBytes = header.Select(c => (byte)c).ToArray();
                                    bufferer.Write(pamHeaderBytes, 0, pamHeaderBytes.Length);

                                    // read the pixels
                                    for (int y = 0; y < height; ++y)
                                    {
                                        if (y % 32 == 31)
                                        {
                                            Console.WriteLine("copying row {0}/{1}", y + 1, height);
                                        }
                                        for (int x = 0; x < width; ++x)
                                        {
                                            for (int c = 0; c < channelCount; ++c)
                                            {
                                                int r = streamDisposal.Streams[c].Read(pixel, 0, pixel.Length);
                                                if (r != bytesPerColorComponent)
                                                {
                                                    throw new Exception("under-read");
                                                }

                                                // invert color (Photoshop -> PAM)
                                                for (int i = 0; i < pixel.Length; ++i)
                                                {
                                                    pixel[i] = (byte)(0xFF - pixel[i]);
                                                }

                                                bufferer.Write(pixel, 0, pixel.Length);
                                            }
                                        }
                                    }
                                }
                    }
                }
            }

            return(0);
        }
Beispiel #2
0
        static void Main(string[] args)
        {
            var psd = new PSDFile();

            using (var readStream = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                psd.Read(readStream);
            }

            Console.WriteLine("Version: {0}", psd.Version);
            Console.WriteLine("Number of channels: {0}", psd.NumberOfChannels);
            Console.WriteLine("Width: {0}", psd.Width);
            Console.WriteLine("Height: {0}", psd.Height);
            Console.WriteLine("Depth: {0}", psd.Depth);
            Console.WriteLine("Color mode: {0}", psd.ColorMode);

            Console.WriteLine("Image resources:");
            foreach (var res in psd.ImageResources)
            {
                Console.WriteLine("  Image resource ID: {0}", res.ID);
                //Console.WriteLine("    Name: {0}", res.Name);

                if (res.ID == ResolutionInfo.ResolutionInfoResourceID)
                {
                    Console.WriteLine("    Binary data:");

                    for (int rowOffset = 0; rowOffset < res.Data.Length; rowOffset += 8)
                    {
                        Console.Write("      ");
                        for (int i = 0; i < 8 && rowOffset + i < res.Data.Length; ++i)
                        {
                            Console.Write("{0:X2} ", res.Data[rowOffset + i]);
                        }
                        Console.WriteLine();
                    }

                    Console.WriteLine("    Resolution info:");
                    var resInfo = ResolutionInfo.FromPSD(psd);
                    Console.WriteLine("      Horizontal resolution: {0} dpi", resInfo.HorizontalResolutionDPI);
                    Console.WriteLine("        Display unit:        {0}", resInfo.HorizontalResolutionDisplayUnit);
                    Console.WriteLine("      Width display unit:    {0}", resInfo.WidthDisplayUnit);
                    Console.WriteLine("      Vertical resolution:   {0} dpi", resInfo.VerticalResolutionDPI);
                    Console.WriteLine("        Display unit:        {0}", resInfo.VerticalResolutionDisplayUnit);
                    Console.WriteLine("      Height display unit:   {0}", resInfo.HeightDisplayUnit);
                }
                else if (res.ID == VersionInfo.VersionInfoResourceID)
                {
                    var verInfo = VersionInfo.FromPSD(psd);
                    Console.WriteLine("    Version info:");
                    Console.WriteLine("      Version: {0}", verInfo.Version);
                    Console.WriteLine("      {0} valid precomposed data", verInfo.HasValidPrecomposedData ? "Has" : "Does not have");
                    Console.WriteLine("      Writer: {0}", verInfo.WriterName);
                    Console.WriteLine("      Reader: {0}", verInfo.ReaderName);
                    Console.WriteLine("      File version: {0}", verInfo.FileVersion);
                }
            }

            Console.WriteLine("Layers: {0}", psd.Layers.Length);
            foreach (PSDLayer layer in psd.Layers)
            {
                Console.WriteLine("    Layer: {0}", layer.Name);
                Console.WriteLine("      Channels: {0}", layer.Channels.Length);
                foreach (PSDLayerChannel chan in layer.Channels)
                {
                    Console.WriteLine("        {0} ({1} bytes {2} from 0x{3:x})", chan.ID, chan.Data.DataLength, chan.Data.Compression, chan.Data.Offset);
                }
                Console.WriteLine("      Additional layer information: {0} entries", layer.AdditionalInformation.Count);
                foreach (PSDAdditionalLayerInformation pali in layer.AdditionalInformation)
                {
                    Console.WriteLine("        Key: {0}", pali.Key);
                    Console.WriteLine("          Data length: {0}", pali.Data.Length);
                }
            }

            if (psd.GlobalLayerMask != null)
            {
                var glm = psd.GlobalLayerMask;
                Console.WriteLine("Global layer mask:");
                Console.WriteLine("  Overlay color space: {0}", glm.OverlayColorSpace);
                Console.WriteLine("  Color components: {0}, {1}, {2}, {3}", glm.ColorComponent1, glm.ColorComponent2, glm.ColorComponent3, glm.ColorComponent4);
                Console.WriteLine("  Opacity: {0}", glm.Opacity);
                Console.WriteLine("  Kind: {0}", glm.Kind);
            }
            else
            {
                Console.WriteLine("No global layer mask");
            }

            Console.WriteLine("Additional layer information: {0} entries", psd.AdditionalLayerInformation.Count);
            foreach (PSDAdditionalLayerInformation ali in psd.AdditionalLayerInformation)
            {
                Console.WriteLine("  Key: {0}", ali.Key);
                Console.WriteLine("    Data length: {0}", ali.Data.Length);
            }

            if (psd.PrecomposedImageData == null)
            {
                Console.WriteLine("No precomposed image data");
            }
            else
            {
                Console.WriteLine("Precomposed image data: {0} from 0x{1:x}", psd.PrecomposedImageData.Compression, psd.PrecomposedImageData.Offset);
            }

            if (args.Length > 1 && args[1] == "-d")
            {
                // decode
                for (int l = 0; l < psd.Layers.Length; ++l)
                {
                    PSDLayer layer = psd.Layers[l];
                    foreach (PSDLayerChannel chan in layer.Channels)
                    {
                        Console.WriteLine("extracting layer {0} channel {1} ({2})", l, chan.ID, chan.Data.Compression);
                        string name = $"{args[0]}.l{l}c{chan.ID}.bin";

                        using (var reader = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.Read))
                            using (var writer = new FileStream(name, FileMode.Create, FileAccess.Write, FileShare.None))
                            {
                                reader.Seek(chan.Data.Offset, SeekOrigin.Begin);
                                switch (chan.Data.Compression)
                                {
                                case CompressionType.RawData:
                                    PixelDataDecoding.DecodeRawData(
                                        reader,
                                        writer,
                                        chan.Data.DataLength
                                        );
                                    break;

                                case CompressionType.PackBits:
                                    PixelDataDecoding.DecodePackBits(
                                        reader,
                                        writer,
                                        scanlineCount: layer.Bottom - layer.Top,
                                        fourByteLengths: psd.Version == 2
                                        );
                                    break;

                                case CompressionType.ZipWithoutPrediction:
                                    PixelDataDecoding.DecodeZip(
                                        reader,
                                        writer,
                                        chan.Data.DataLength
                                        );
                                    break;

                                case CompressionType.ZipWithPrediction:
                                    PixelDataDecoding.DecodeZipPredicted(
                                        reader,
                                        writer,
                                        chan.Data.DataLength,
                                        psd.Depth,
                                        psd.Width
                                        );
                                    break;
                                }
                            }
                    }
                }

                if (psd.PrecomposedImageData != null)
                {
                    string compositeName = $"{args[0]}.composite.bin";
                    using (var reader = new FileStream(args[0], FileMode.Open, FileAccess.Read, FileShare.Read))
                        using (var writer = new FileStream(compositeName, FileMode.Create, FileAccess.Write, FileShare.None))
                        {
                            reader.Seek(psd.PrecomposedImageData.Offset, SeekOrigin.Begin);
                            switch (psd.PrecomposedImageData.Compression)
                            {
                            case CompressionType.RawData:
                                PixelDataDecoding.DecodeRawData(
                                    reader,
                                    writer,
                                    length: null
                                    );
                                break;

                            case CompressionType.PackBits:
                                PixelDataDecoding.DecodePackBits(
                                    reader,
                                    writer,
                                    scanlineCount: psd.Height * psd.NumberOfChannels,
                                    fourByteLengths: psd.Version == 2
                                    );
                                break;

                            case CompressionType.ZipWithoutPrediction:
                                PixelDataDecoding.DecodeZip(
                                    reader,
                                    writer,
                                    length: null
                                    );
                                break;

                            case CompressionType.ZipWithPrediction:
                                PixelDataDecoding.DecodeZipPredicted(
                                    reader,
                                    writer,
                                    length: null,
                                    depth: psd.Depth,
                                    imageWidth: psd.Width
                                    );
                                break;
                            }
                        }
                }
            }
        }