/// <summary> /// Store layer metadata and image data. /// </summary> public static void StoreLayer(BitmapLayer layer, PhotoshopFile.Layer psdLayer, PsdSaveConfigToken psdToken) { // Set layer metadata psdLayer.Name = layer.Name; psdLayer.Rect = FindImageRectangle(layer.Surface); psdLayer.BlendModeKey = PsdBlendMode.Normal; psdLayer.Opacity = layer.Opacity; psdLayer.Visible = layer.Visible; psdLayer.Masks = new MaskInfo(); psdLayer.BlendingRangesData = new BlendingRanges(psdLayer); // Store channel metadata int layerSize = psdLayer.Rect.Width * psdLayer.Rect.Height; for (int i = -1; i < 3; i++) { var ch = new Channel((short)i, psdLayer); ch.ImageCompression = psdToken.RleCompress ? ImageCompression.Rle : ImageCompression.Raw; ch.ImageData = new byte[layerSize]; psdLayer.Channels.Add(ch); } // Store and compress channel image data var channelsArray = psdLayer.Channels.ToIdArray(); StoreLayerImage(channelsArray, psdLayer.AlphaChannel, layer.Surface, psdLayer.Rect); }
protected PsdSaveConfigToken(PsdSaveConfigToken copyMe) { this.RleCompress = copyMe.RleCompress; }
public static void Save(Document input, Stream output, PsdSaveConfigToken psdToken, Surface scratchSurface, ProgressEventHandler progressCallback) { var psdVersion = ((input.Height > 30000) || (input.Width > 30000)) ? PsdFileVersion.PsbLargeDocument : PsdFileVersion.Psd; var psdFile = new PsdFile(psdVersion); psdFile.RowCount = input.Height; psdFile.ColumnCount = input.Width; // We only save in RGBA format, 8 bits per channel, which corresponds to // Paint.NET's internal representation. psdFile.ChannelCount = 4; psdFile.ColorMode = PsdColorMode.RGB; psdFile.BitDepth = 8; psdFile.ImageCompression = psdToken.RleCompress ? ImageCompression.Rle : ImageCompression.Raw; // Treat the composite image as another layer when reporting progress. var progress = new ProgressNotifier(progressCallback); var percentPerLayer = percentStoreImages / (input.Layers.Count + 1); // Render the composite image. This operation is parallelized within // Paint.NET using its own private thread pool. using (var ra = new RenderArgs(scratchSurface)) { input.Flatten(scratchSurface); progress.Notify(percentRenderComposite); } // Delegate to store the composite Action storeCompositeAction = () => { // Allocate space for the composite image data int imageSize = psdFile.RowCount * psdFile.ColumnCount; for (short i = 0; i < psdFile.ChannelCount; i++) { var channel = new Channel(i, psdFile.BaseLayer); channel.ImageData = new byte[imageSize]; channel.ImageCompression = psdFile.ImageCompression; psdFile.BaseLayer.Channels.Add(channel); } var channelsArray = psdFile.BaseLayer.Channels.ToIdArray(); StoreLayerImage(channelsArray, channelsArray[3], scratchSurface, psdFile.BaseLayer.Rect); progress.Notify(percentPerLayer); }; // Delegate to store the layers Action storeLayersAction = () => { // LayerList is an ArrayList, so we have to cast to get a generic // IEnumerable that works with LINQ. var pdnLayers = input.Layers.Cast <BitmapLayer>(); var psdLayers = pdnLayers.AsParallel().AsOrdered().Select(pdnLayer => { var psdLayer = new PhotoshopFile.Layer(psdFile); StoreLayer(pdnLayer, psdLayer, psdToken); progress.Notify(percentPerLayer); return(psdLayer); }); psdFile.Layers.AddRange(psdLayers); }; // Process composite and layers in parallel Parallel.Invoke(storeCompositeAction, storeLayersAction); psdFile.Save(output, Encoding.Default); }