/// <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 = layer.BlendMode.ToPsdBlendMode(); 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); }
public SaveLayerPixelsContext(BitmapLayer layer, PsdFile psdFile, Document input, PhotoshopFile.Layer psdLayer, PsdSaveConfigToken psdToken) { this.layer = layer; this.psdFile = psdFile; this.input = input; this.psdToken = psdToken; this.psdLayer = psdLayer; }
/// <summary> /// Store layer metadata and image data. /// </summary> public static void StoreLayer( BitmapLayerInfo layerInfo, PhotoshopFile.Layer psdLayer, PsdSaveConfigToken psdToken) { var layer = layerInfo.Layer; // Set layer metadata psdLayer.Name = layerInfo.Name; psdLayer.Rect = FindImageRectangle(layer.Surface); psdLayer.Opacity = layer.Opacity; 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); } psdLayer.BlendModeKey = layer.BlendMode.ToPsdBlendMode(); psdLayer.Visible = layer.Visible; // Create folders/groups before actual image data will be saved. if (psdToken.SaveLayers && !layerInfo.RenderAsRegularLayer) { if (layerInfo.IsGroupStart) { var status = layer.Visible ? LayerSectionType.OpenFolder : LayerSectionType.ClosedFolder; psdLayer.AdditionalInfo.Add(new LayerSectionInfo(status)); psdLayer.Name = layerInfo.Name; psdLayer.Visible = true; } else if (layerInfo.IsGroupEnd) { // End of the group psdLayer.Opacity = 255; psdLayer.BlendModeKey = PsdBlendMode.PassThrough; psdLayer.Name = "</Layer group>"; psdLayer.AdditionalInfo.Add(new LayerSectionInfo(LayerSectionType.SectionDivider)); } return; } // Store and compress channel image data var channelsArray = psdLayer.Channels.ToIdArray(); StoreLayerImage(channelsArray, psdLayer.AlphaChannel, layer.Surface, psdLayer.Rect); }
protected override void InitWidgetFromToken(SaveConfigToken token) { if (token is PsdSaveConfigToken) { PsdSaveConfigToken psdToken = (PsdSaveConfigToken)token; this.m_rleCompressCheckBox.Checked = psdToken.RleCompress; } else { this.m_rleCompressCheckBox.Checked = true; } }
protected PsdSaveConfigToken(PsdSaveConfigToken copyMe) { this.RleCompress = copyMe.RleCompress; this.SaveLayers = copyMe.SaveLayers; }
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.Resolution = GetResolutionInfo(input); 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>(); // Create folders/groups before actual image data will be saved. var layerInfos = PrepareLayers(pdnLayers, psdToken.SaveLayers); var psdLayers = layerInfos.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); }
protected override void OnSave(Document input, System.IO.Stream output, SaveConfigToken token, Surface scratchSurface, ProgressEventHandler callback) { PsdSaveConfigToken psdToken = (PsdSaveConfigToken)token; PsdFile psdFile = new PsdFile(); //----------------------------------------------------------------------- psdFile.Rows = input.Height; psdFile.Columns = input.Width; // we have an Alpha channel which will be saved, // we have to add this to our image resources psdFile.Channels = 4; // for now we oly save the images as RGB psdFile.ColorMode = PsdFile.ColorModes.RGB; psdFile.Depth = 8; //----------------------------------------------------------------------- // no color mode Data //----------------------------------------------------------------------- ResolutionInfo resInfo = new ResolutionInfo(); resInfo.HeightUnit = ResolutionInfo.Unit.In; resInfo.WidthUnit = ResolutionInfo.Unit.In; if (input.DpuUnit == MeasurementUnit.Inch) { resInfo.HResUnit = ResolutionInfo.ResUnit.PxPerInch; resInfo.VResUnit = ResolutionInfo.ResUnit.PxPerInch; resInfo.HRes = (short)input.DpuX; resInfo.VRes = (short)input.DpuY; } else { resInfo.HResUnit = ResolutionInfo.ResUnit.PxPerCent; resInfo.VResUnit = ResolutionInfo.ResUnit.PxPerCent; resInfo.HRes = (short)(input.DpuX / 2.54); resInfo.VRes = (short)(input.DpuY / 2.54); } psdFile.Resolution = resInfo; //----------------------------------------------------------------------- psdFile.ImageCompression = psdToken.RleCompress ? ImageCompression.Rle : ImageCompression.Raw; int size = psdFile.Rows * psdFile.Columns; psdFile.ImageData = new byte[psdFile.Channels][]; for (int i = 0; i < psdFile.Channels; i++) { psdFile.ImageData[i] = new byte[size]; } using (RenderArgs ra = new RenderArgs(scratchSurface)) { input.Flatten(scratchSurface); } unsafe { for (int y = 0; y < psdFile.Rows; y++) { int rowIndex = y * psdFile.Columns; ColorBgra *srcRow = scratchSurface.GetRowAddress(y); ColorBgra *srcPixel = srcRow; for (int x = 0; x < psdFile.Columns; x++) { int pos = rowIndex + x; psdFile.ImageData[0][pos] = srcPixel->R; psdFile.ImageData[1][pos] = srcPixel->G; psdFile.ImageData[2][pos] = srcPixel->B; psdFile.ImageData[3][pos] = srcPixel->A; srcPixel++; } } } PaintDotNet.Threading.PrivateThreadPool threadPool = new PaintDotNet.Threading.PrivateThreadPool(); foreach (BitmapLayer layer in input.Layers) { PhotoshopFile.Layer psdLayer = new PhotoshopFile.Layer(psdFile); BlendOpToBlendModeKey(layer.BlendOp, psdLayer); SaveLayerPixelsContext slc = new SaveLayerPixelsContext(layer, psdFile, input, psdLayer, psdToken); WaitCallback waitCallback = new WaitCallback(slc.SaveLayer); threadPool.QueueUserWorkItem(waitCallback); } threadPool.Drain(); psdFile.Save(output); }
public static void SaveLayerPixels(BitmapLayer layer, PsdFile psdFile, Document input, PhotoshopFile.Layer psdLayer, PsdSaveConfigToken psdToken) { Surface surface = layer.Surface; int rectLeft = input.Width; int rectTop = input.Height; int rectRight = 0; int rectBottom = 0; // Determine the real size of this layer, i.e., the smallest rectangle // that includes all all non-invisible pixels unsafe { for (int y = 0; y < psdFile.Rows; y++) { int rowIndex = y * psdFile.Columns; ColorBgra *srcRow = surface.GetRowAddress(y); ColorBgra *srcPixel = srcRow; for (int x = 0; x < psdFile.Columns; x++) { int pos = rowIndex + x; // Found a non-transparent pixel, potentially increase the size of the rectangle if (srcPixel->A > 0) { // Expand the rectangle if (x < rectLeft) { rectLeft = x; } if (x > rectRight) { rectRight = x; } if (y < rectTop) { rectTop = y; } if (y > rectBottom) { rectBottom = y; } } srcPixel++; } } } psdLayer.Rect = new Rectangle(rectLeft, rectTop, rectRight - rectLeft + 1, rectBottom - rectTop + 1); psdLayer.Name = layer.Name; psdLayer.Opacity = layer.Opacity; psdLayer.Visible = layer.Visible; psdLayer.MaskData = new PhotoshopFile.Layer.Mask(psdLayer); psdLayer.BlendingRangesData = new PhotoshopFile.Layer.BlendingRanges(psdLayer); int layerSize = psdLayer.Rect.Width * psdLayer.Rect.Height; for (int i = -1; i < 3; i++) { PhotoshopFile.Layer.Channel ch = new PhotoshopFile.Layer.Channel((short)i, psdLayer); ch.ImageCompression = psdToken.RleCompress ? ImageCompression.Rle : ImageCompression.Raw; ch.ImageData = new byte[layerSize]; } var channels = psdLayer.ChannelsArray; var alphaChannel = psdLayer.AlphaChannel; unsafe { int rowIndex = 0; for (int y = 0; y < psdLayer.Rect.Height; y++) { ColorBgra *srcRow = surface.GetRowAddress(y + psdLayer.Rect.Top); ColorBgra *srcPixel = srcRow + psdLayer.Rect.Left; for (int x = 0; x < psdLayer.Rect.Width; x++) { int pos = rowIndex + x; channels[0].ImageData[pos] = srcPixel->R; channels[1].ImageData[pos] = srcPixel->G; channels[2].ImageData[pos] = srcPixel->B; alphaChannel.ImageData[pos] = srcPixel->A; srcPixel++; } rowIndex += psdLayer.Rect.Width; } } }
protected PsdSaveConfigToken(PsdSaveConfigToken copyMe) { this.m_rleCompress = copyMe.m_rleCompress; }