public static unsafe void Save(Document input, Stream output, PropertyBasedSaveConfigToken token, ProgressEventHandler progressCallback) { bool rle = token.GetProperty <PaintDotNet.PropertySystem.BooleanProperty>(PropertyNames.RLE).Value; AbrFileVersion fileVersion = (AbrFileVersion)token.GetProperty(PropertyNames.FileVersion).Value; double progressPercentage = 0.0; double progressDelta = (1.0 / input.Layers.Count) * 100.0; using (BinaryReverseWriter writer = new BinaryReverseWriter(output, true)) { writer.Write((short)fileVersion); writer.Write((short)input.Layers.Count); foreach (Layer item in input.Layers) { BitmapLayer layer = (BitmapLayer)item; SaveLayer(writer, layer, fileVersion, rle); progressPercentage += progressDelta; progressCallback(null, new ProgressEventArgs(progressPercentage)); } } }
private static void SaveLayer(BinaryReverseWriter writer, BitmapLayer layer, AbrFileVersion fileVersion, bool rle) { writer.Write((short)AbrBrushType.Sampled); using (new LengthWriter(writer)) { // Write the miscellaneous data, unused. writer.Write(0); // Write the spacing. string spacingMetaData = layer.Metadata.GetUserValue(AbrMetadataNames.BrushSpacing); if (spacingMetaData != null && int.TryParse(spacingMetaData, NumberStyles.Number, CultureInfo.InvariantCulture, out int spacing)) { writer.Write((short)spacing); } else { writer.Write((short)DefaultSpacingPercent); } // Write the brush name, if applicable. if (fileVersion == AbrFileVersion.Version2) { writer.WriteUnicodeString(layer.Name); } Rectangle imageBounds = GetImageRectangle(layer.Surface); // Write the anti-aliasing. if (imageBounds.Width < 32 && imageBounds.Height < 32) { // Only brushes less than 32x32 pixels are anti-aliased by Photoshop. writer.Write((byte)1); } else { writer.Write((byte)0); } // Write the Int16 bounds. writer.WriteInt16Rectangle(imageBounds); // Write the Int32 bounds. writer.WriteInt32Rectangle(imageBounds); // Write the depth. writer.Write((short)8); byte[] alpha = GetBrushAlphaData(layer.Surface, imageBounds); int rowsRemaining = imageBounds.Height; int rowsRead = 0; do { // Brushes taller than 16384 pixels are split into 16384 line chunks. int chunkHeight = Math.Min(rowsRemaining, 16384); if (rle) { // Write the RLE compressed header. writer.Write((byte)AbrImageCompression.RLE); long rowCountOffset = writer.BaseStream.Position; for (int i = 0; i < chunkHeight; i++) { // Placeholder for the row byte count. writer.Write(short.MaxValue); } short[] rowByteCount = new short[chunkHeight]; for (int y = 0; y < chunkHeight; y++) { int row = rowsRead + y; rowByteCount[y] = (short)RLEHelper.EncodedRow(writer.BaseStream, alpha, row * imageBounds.Width, imageBounds.Width); } long current = writer.BaseStream.Position; writer.BaseStream.Position = rowCountOffset; for (int i = 0; i < chunkHeight; i++) { writer.Write(rowByteCount[i]); } writer.BaseStream.Position = current; } else { // Write the uncompressed header. writer.Write((byte)AbrImageCompression.Raw); for (int y = 0; y < chunkHeight; y++) { int row = rowsRead + y; writer.Write(alpha, row * imageBounds.Width, imageBounds.Width); } } rowsRemaining -= 16384; rowsRead += 16384; } while (rowsRemaining > 0); } }