unsafe static void ReadRawBitmapData(Surface surface, Stream destinationStream, Byte bpp) { Int32 stride = MbmFile.GetStride(surface.Width, (Byte)bpp); for (int y = 0; y < surface.Height; y++) { destinationStream.Position = stride * y; ColorBgra *row = surface.GetRowAddress(y); for (int x = 0; x < surface.Width; x++) { destinationStream.WriteByte(row[x].R); if (bpp >= 16) { destinationStream.WriteByte(row[x].G); } if (bpp >= 24) { destinationStream.WriteByte(row[x].B); } if (bpp >= 32) { destinationStream.WriteByte(row[x].A); } } for (int x = surface.Width * (bpp / 8); x < stride; x++) { destinationStream.WriteByte(255); } } }
public static Document Load(MbmFile file, Stream source) { // TODO: Make this configurable int bitmapToLoad = 0; return(LoadIndex(file, source, bitmapToLoad)); }
/// <summary> /// Load bitmap data to Paint.NET surface. /// </summary> /// <param name="header">The bitmap header to convert.</param> /// <param name="offset">The offset of the header in the stream.</param> /// <param name="source">The source stream.</param> /// <param name="surface">Paint.NET surface.</param> unsafe static void LoadBitmapToSurface(SBMHeader header, UInt32 offset, Stream source, Surface surface) { UInt32 lastPos = (UInt32)source.Position; source.Seek(offset + header.headerLength, SeekOrigin.Begin); UInt32 compressedSize = header.bitmapSize - header.headerLength; UInt32 stride = (UInt32)MbmFile.GetStride(header.sizeInPixel.Width, (Byte)(header.bitsPerPixel)); UInt32 realSize = (UInt32)header.sizeInPixel.Height * stride; byte[] decompressedData = new byte[realSize]; MemoryStream destinationStream = new MemoryStream(decompressedData, 0, (int)realSize); destinationStream.Seek(0, SeekOrigin.Begin); switch (header.compression) { case BitmapCompression comp when(comp <= BitmapCompression.ThirtyTwoABitsRLE && comp > BitmapCompression.None): Algorithm.RLEDecompressor.Decompress(destinationStream, source, (int)compressedSize, comp); break; default: source.Read(decompressedData, 0, (int)realSize); break; } destinationStream.Seek(0, SeekOrigin.Begin); for (int y = 0; y < header.sizeInPixel.Height; y++) { destinationStream.Seek(stride * y, SeekOrigin.Begin); ColorBgra *rowData = surface.GetRowAddress(y); // Convert to PDN representation switch (header.bitsPerPixel) { // These case are generally easy // Each channel is represent by 8 bits case UInt32 i when(i % 8 == 0): for (int x = 0; x < header.sizeInPixel.Width; x++) { rowData[x].R = (i >= 8) ? (Byte)destinationStream.ReadByte() : (Byte)0; rowData[x].G = (i >= 16) ? (Byte)destinationStream.ReadByte() : (Byte)0; rowData[x].B = (i >= 24) ? (Byte)destinationStream.ReadByte() : (Byte)0; rowData[x].A = (i >= 32) ? (Byte)destinationStream.ReadByte() : (Byte)255; } break; default: throw new MbmException(String.Format("Unhandled converting {0} bits per pixel to Paint.NET surface", header.bitsPerPixel)); } } source.Seek(lastPos, SeekOrigin.Begin); }
static BitmapCompression ChooseBestCompressionMode(int width, int height, UInt32 bpp, BitmapColor colorMode) { Int32 stride = MbmFile.GetStride(width, (Byte)bpp); if (width * height <= MaximumSizeNoCompress) { // At this size we should not compress. Not worth at all return(BitmapCompression.None); } switch (colorMode) { case BitmapColor.ColorWithAlpha: return(BitmapCompression.ThirtyTwoABitsRLE); case BitmapColor.ColorWithAlphaPM: return(BitmapCompression.ThirtyTwoABitsRLE); case BitmapColor.Color: break; default: throw new MbmException(String.Format("Unsupported bitmap color type to save: {0}", colorMode.ToString())); } switch (bpp) { case 32: return(BitmapCompression.ThirtyTwoUBitsRLE); case 24: return(BitmapCompression.TwentyFourBitsRLE); case 16: return(BitmapCompression.SixteenBitsRLE); case 12: return(BitmapCompression.TwelveBitsRLE); case 8: return(BitmapCompression.ByteRLE); default: break; } throw new MbmException(String.Format("Unsupported bits per pixel to save: {0}", bpp)); }
static Document LoadIndex(MbmFile file, Stream source, int index) { SBMHeader header = file.GetBitmapHeader(index); UInt32 headerOffset = file.GetBitmapHeaderOffset(index); Document doc = new Document(header.sizeInPixel); // Create background layer BitmapLayer layer = Layer.CreateBackgroundLayer(header.sizeInPixel.Width, header.sizeInPixel.Height, "Bitmap"); Surface surface = layer.Surface; LoadBitmapToSurface(header, headerOffset, source, surface); doc.Layers.Add(layer); return(doc); }
protected override Document OnLoad(Stream fileStream) { MbmFile file = new MbmFile(fileStream); return(MbmLoad.Load(file, fileStream)); }
public static void Save(Document input, Stream output, Surface scratchSurface, UInt32 bpp, BitmapColor colorMode) { // Flatten the image first input.Flatten(scratchSurface); // Build up SBM Header SBMHeader header = new SBMHeader(); header.headerLength = 40; header.sizeInPixel.Width = input.Width; header.sizeInPixel.Height = input.Height; header.sizeInTwips.Width = input.Width * 15; header.sizeInTwips.Height = input.Height * 15; header.colorMode = colorMode; header.bitsPerPixel = bpp; // Determine what compression to use BitmapCompression compressionToUse = ChooseBestCompressionMode(input.Width, input.Height, bpp, colorMode); header.compression = compressionToUse; Int32 stride = MbmFile.GetStride(input.Width, (Byte)bpp); byte[] rawBitmapData = new byte[stride * input.Height]; byte[] compressedBitmapData = null; MemoryStream bitmapStream = new MemoryStream(rawBitmapData, 0, rawBitmapData.Length); ReadRawBitmapData(scratchSurface, bitmapStream, (Byte)bpp); bitmapStream.Seek(0, SeekOrigin.Begin); switch (compressionToUse) { case BitmapCompression comp when(comp >= BitmapCompression.ByteRLE && comp <= BitmapCompression.ThirtyTwoABitsRLE): compressedBitmapData = new byte[stride * input.Height]; MemoryStream compressedDestStream = new MemoryStream(compressedBitmapData, 0, compressedBitmapData.Length); compressedDestStream.Seek(0, SeekOrigin.Begin); Algorithm.RLECompressor.Compress(compressedDestStream, bitmapStream, stride * input.Height, compressionToUse); header.bitmapSize = (UInt32)(40 + compressedDestStream.Position); break; case BitmapCompression.None: header.bitmapSize = (UInt32)(40 + stride * input.Height); break; default: throw new MbmException(String.Format("Unsupported bitmap compression type {0}", compressionToUse.ToString())); } output.Seek(/*sizeof(MbmHeader)*/ 20, SeekOrigin.Begin); MbmBinaryWriter writer = new MbmBinaryWriter(output); // Try to write our header writer.WriteUInt32(header.bitmapSize); writer.WriteUInt32(header.headerLength); writer.WriteSize(header.sizeInPixel); writer.WriteSize(header.sizeInTwips); writer.WriteUInt32(header.bitsPerPixel); writer.WriteUInt32((UInt32)header.colorMode); writer.WriteUInt32(header.paletteSize); writer.WriteUInt32((UInt32)header.compression); // Write data switch (compressionToUse) { case BitmapCompression comp when(comp >= BitmapCompression.ByteRLE && comp <= BitmapCompression.ThirtyTwoABitsRLE): output.Write(compressedBitmapData, 0, (int)(header.bitmapSize - header.headerLength)); break; case BitmapCompression.None: output.Write(rawBitmapData, 0, rawBitmapData.Length); break; default: break; } // Write trailer UInt32 trailerOffset = writer.Tell(); writer.WriteUInt32(1); writer.WriteUInt32(20); // Seek back and write our header writer.Seek(0); writer.WriteUInt32(MbmHeader.directFileStoreUIDNum); writer.WriteUInt32(MbmHeader.multiBitmapUIDNum); writer.WriteUInt32(0); writer.WriteUInt32(0); // Checksum writer.WriteUInt32(trailerOffset); }