static string SaveBitmap(string origName, RawFormat format, Size size, byte[] data) { string rawName = Path.ChangeExtension(origName, $"{format}-{size.Width}x{size.Height}.raw"); File.WriteAllBytes(rawName, data); return(rawName); }
public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams) { if (filename == null) { throw new ArgumentNullException("filename"); } if (encoder == null) { throw new ArgumentNullException("encoder"); } IntPtr encoderParamsMemory = IntPtr.Zero; if (encoderParams != null) { _rawData = null; encoderParamsMemory = encoderParams.ConvertToMemory(); } int status = SafeNativeMethods.Gdip.Ok; try { Guid g = encoder.Clsid; bool saved = false; if (_rawData != null) { ImageCodecInfo rawEncoder = RawFormat.FindEncoder(); if (rawEncoder != null && rawEncoder.Clsid == g) { using (FileStream fs = File.OpenWrite(filename)) { fs.Write(_rawData, 0, _rawData.Length); saved = true; } } } if (!saved) { status = SafeNativeMethods.Gdip.GdipSaveImageToFile(new HandleRef(this, nativeImage), filename, ref g, new HandleRef(encoderParams, encoderParamsMemory)); } } finally { if (encoderParamsMemory != IntPtr.Zero) { Marshal.FreeHGlobal(encoderParamsMemory); } } if (status != SafeNativeMethods.Gdip.Ok) { throw SafeNativeMethods.Gdip.StatusException(status); } }
/// <summary> /// Saves this <see cref='Image'/> to the specified file in the specified format and with the specified encoder parameters. /// </summary> public void Save(string filename, ImageCodecInfo encoder, EncoderParameters encoderParams) { if (filename == null) { throw new ArgumentNullException(nameof(filename)); } if (encoder == null) { throw new ArgumentNullException(nameof(encoder)); } if (!Directory.Exists(Path.GetDirectoryName(filename))) { throw new DirectoryNotFoundException(SR.Format(SR.IO_PathNotFound_Path, filename)); } IntPtr encoderParamsMemory = IntPtr.Zero; if (encoderParams != null) { _rawData = null; encoderParamsMemory = encoderParams.ConvertToMemory(); } try { Guid g = encoder.Clsid; bool saved = false; if (_rawData != null) { ImageCodecInfo rawEncoder = RawFormat.FindEncoder(); if (rawEncoder != null && rawEncoder.Clsid == g) { using (FileStream fs = File.OpenWrite(filename)) { fs.Write(_rawData, 0, _rawData.Length); saved = true; } } } if (!saved) { Gdip.CheckStatus(Gdip.GdipSaveImageToFile( new HandleRef(this, nativeImage), filename, ref g, new HandleRef(encoderParams, encoderParamsMemory))); } } finally { if (encoderParamsMemory != IntPtr.Zero) { Marshal.FreeHGlobal(encoderParamsMemory); } } }
/// <summary> /// Saves this <see cref='Image'/> to the specified stream in the specified format. /// </summary> public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters?encoderParams) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (encoder == null) { throw new ArgumentNullException(nameof(encoder)); } IntPtr encoderParamsMemory = IntPtr.Zero; if (encoderParams != null) { _rawData = null; encoderParamsMemory = encoderParams.ConvertToMemory(); } try { Guid g = encoder.Clsid; bool saved = false; if (_rawData != null) { ImageCodecInfo?rawEncoder = RawFormat.FindEncoder(); if (rawEncoder != null && rawEncoder.Clsid == g) { stream.Write(_rawData, 0, _rawData.Length); saved = true; } } if (!saved) { using DrawingCom.IStreamWrapper streamWrapper = DrawingCom.GetComWrapper(new GPStream(stream, makeSeekable: false)); unsafe { Gdip.CheckStatus(Gdip.GdipSaveImageToStream( new HandleRef(this, nativeImage), streamWrapper.Ptr, &g, new HandleRef(encoderParams, encoderParamsMemory))); } } } finally { if (encoderParamsMemory != IntPtr.Zero) { Marshal.FreeHGlobal(encoderParamsMemory); } } }
/// <include file='doc\Image.uex' path='docs/doc[@for="Image.Save4"]/*' /> /// <devdoc> /// <para> /// Saves this <see cref='System.Drawing.Image'/> to the specified stream in the specified /// format. /// </para> /// </devdoc> public void Save(Stream stream, ImageCodecInfo encoder, EncoderParameters encoderParams) { if (stream == null) { throw new ArgumentNullException("stream"); } if (encoder == null) { throw new ArgumentNullException("encoder"); } IntPtr encoderParamsMemory = IntPtr.Zero; if (encoderParams != null) { rawData = null; encoderParamsMemory = encoderParams.ConvertToMemory(); } int status = SafeNativeMethods.Ok; try { Guid g = encoder.Clsid; bool saved = false; if (rawData != null) { ImageCodecInfo rawEncoder = RawFormat.FindEncoder(); if (rawEncoder.Clsid == g) { stream.Write(rawData, 0, rawData.Length); saved = true; } } if (!saved) { status = SafeNativeMethods.GdipSaveImageToStream(new HandleRef(this, nativeImage), new UnsafeNativeMethods.ComStreamFromDataStream(stream), ref g, new HandleRef(encoderParams, encoderParamsMemory)); } } finally { if (encoderParamsMemory != IntPtr.Zero) { Marshal.FreeHGlobal(encoderParamsMemory); } } if (status != SafeNativeMethods.Ok) { throw SafeNativeMethods.StatusException(status); } }
/// <summary> /// NEW!! So far alright? /// </summary> public void SegwitTestNet() { //The faucet: https://testnet.manu.backend.hamburg/faucet //https://bitcoin.stackexchange.com/questions/59231/how-to-sign-a-segwit-transaction-via-nbitcoin var net = NBitcoin.Network.TestNet; BlockExplorer explorer = new BlockExplorer("https://testnet.blockexplorer.com/"); HDWallet wallet = new HDWallet("seed12345678ryan12345678", net); uint path = 0; var extkey = wallet.GetPrivateKey(path); Key k = extkey.PrivateKey; //This gives you a Bech32 address (currently not really interoperable in wallets, so you need to convert it into P2SH) var address = k.PubKey.WitHash.GetAddress(net); var p2sh = address.GetScriptAddress(); //p2sh is now an interoperable P2SH segwit address //SENT TO: 2NGSoYM3yLi9SXvZc5yYcSwHWyCWzgBrP9X //TXID 1397a4cc480879eae604ce871c47a4d690c6ea6a6dcfd7e38d95f31b81593556 var response = explorer.GetUnspent(p2sh.ToString()); List <ExplorerUnspent> unspent = response.Convert <List <ExplorerUnspent> >(); List <Transaction> transactions = new List <Transaction>(); foreach (var item in unspent) { string txcontent = "{\"txid\":\"6636b3fedb57be81232f92f80fa8d3df9a0f07305af2c7f705a7f353e516b1d7\",\"version\":1,\"locktime\":0,\"vin\":[{\"txid\":\"50b7d9a5fa1281e7020fa8a152835756e0d57d6b4d634b4251ab500e8630bc3e\",\"vout\":1,\"scriptSig\":{\"asm\":\"0014a16f4ba22e84c364ec4f8fe19d8a48762156b41e\",\"hex\":\"160014a16f4ba22e84c364ec4f8fe19d8a48762156b41e\"},\"sequence\":4294967295,\"n\":0,\"addr\":\"2N9viNVJ5MsAM8MXdUuATDwKeMjMLQkaXyR\",\"valueSat\":193987962850,\"value\":1939.8796285,\"doubleSpentTxID\":null}],\"vout\":[{\"value\":\"2.00000000\",\"n\":0,\"scriptPubKey\":{\"hex\":\"a9142f672b3ea4af55d9da43e507e3c060d2e34521ba87\",\"asm\":\"OP_HASH160 2f672b3ea4af55d9da43e507e3c060d2e34521ba OP_EQUAL\",\"addresses\":[\"2MwZsLbuB328gxHRfr1VDrfDrK6aWicQcAW\"],\"type\":\"scripthash\"},\"spentTxId\":null,\"spentIndex\":null,\"spentHeight\":null},{\"value\":\"1937.87862850\",\"n\":1,\"scriptPubKey\":{\"hex\":\"a914859695c2cb37ee30bf6a18943c8c27a3ac0e6faa87\",\"asm\":\"OP_HASH160 859695c2cb37ee30bf6a18943c8c27a3ac0e6faa OP_EQUAL\",\"addresses\":[\"2N5RaFdK3rgsNayXnkTQaSLKVBB3brW7G4m\"],\"type\":\"scripthash\"},\"spentTxId\":\"892e6facc4fc596852d41af367dd41f7a7ec1b11a319a202bc0d4c5b8f792f2f\",\"spentIndex\":0,\"spentHeight\":1255964}],\"blockhash\":\"000000000007dbc3ffd03559f2192b299bc0c20aa0aea8e1939f2731e01103e8\",\"blockheight\":1255946,\"confirmations\":123,\"time\":1514228293,\"blocktime\":1514228293,\"valueOut\":1939.8786285,\"size\":138,\"valueIn\":1939.8796285,\"fees\":0.001}"; ExplorerResponse txResponse = explorer.GetTransaction(item.txid); RawFormat format = RawFormat.Satoshi; var tx = Transaction.Parse(txResponse.data, format, net); transactions.Add(tx); } //For spending, it works the same as a a normal P2SH //You need to get the ScriptCoin, the RedeemScript of you script coin should be k.PubKey.WitHash.ScriptPubKey. var redeemScript = k.PubKey.WitHash.ScriptPubKey; var redeemScript = k.PubKey.WitHash.ScriptPubKey; Transaction received = transactions[0]; ScriptCoin coin = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript); //1397a4cc480879eae604ce871c47a4d690c6ea6a6dcfd7e38d95f31b81593556 BitcoinAddress destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(coin); builder.AddKeys(k); builder.Send(destination, Money.Coins(1.99m)); builder.SendFees(Money.Coins(0.001m)); builder.SetChange(p2sh); var signedTx = builder.BuildTransaction(true); Console.WriteLine(signedTx.ToHex()); string x = ";;"; //Assert.True(builder.Verify(signedTx)); }
void ISerializable.GetObjectData(SerializationInfo si, StreamingContext context) { using (MemoryStream ms = new MemoryStream()) { // Icon is a decoder-only codec if (RawFormat.Equals(ImageFormat.Icon)) { Save(ms, ImageFormat.Png); } else { Save(ms, RawFormat); } si.AddValue("Data", ms.ToArray()); } }
static private RawFormatter GetFormatter(RawFormat rawFormat, Network network) { RawFormatter formatter = null; switch (rawFormat) { case RawFormat.Satoshi: formatter = new SatoshiFormatter(); break; case RawFormat.BlockExplorer: formatter = new BlockExplorerFormatter(); break; default: throw new NotSupportedException(rawFormat.ToString()); } formatter.Network = network ?? formatter.Network; return(formatter); }
public string ToString(RawFormat rawFormat, Network network = null) { var formatter = GetFormatter(rawFormat, network); return(ToString(formatter)); }
public static Transaction Parse(string tx, RawFormat format, Network network = null) { return(GetFormatter(format, network).ParseJson(tx)); }
private static RawFormatter GetFormatter(RawFormat rawFormat, Network network) { RawFormatter formatter = null; switch(rawFormat) { case RawFormat.Satoshi: formatter = new SatoshiFormatter(); break; case RawFormat.BlockExplorer: formatter = new BlockExplorerFormatter(); break; default: throw new NotSupportedException(rawFormat.ToString()); } formatter.Network = network ?? formatter.Network; return formatter; }
public static Transaction Parse(string tx, RawFormat format, Network network = null) { return GetFormatter(format, network).Parse(tx); }
public void CreateBasicSwap(uint path, params string[] seed) { List <ExtKey> keys = new List <ExtKey>(); Segwit segwit = new Segwit(NBitcoin.Network.TestNet); for (int i = 0; i < seed.Length; i++) { var key = GetKey(path, seed[i]); var address = segwit.GetP2SHAddress(key); keys.Add(key); //Console.WriteLine(address.ToString()); } MultiSig multi = new MultiSig(NBitcoin.Network.TestNet); var p2sh = multi.GetP2SHAddress(2, keys.ToArray()); Console.WriteLine(p2sh.ToString()); //multi: b2366808fa2396a5a32120a27f55571055491d6ff8e6bd1e31e52bdd14b91dfb REST.BlockExplorer explorer = new REST.BlockExplorer("https://testnet.blockexplorer.com/"); //var tx = explorer.GetTransaction("b2366808fa2396a5a32120a27f55571055491d6ff8e6bd1e31e52bdd14b91dfb"); var response = explorer.GetUnspent(p2sh.ToString()); List <ExplorerUnspent> unspent = response.Convert <List <ExplorerUnspent> >(); List <Transaction> transactions = new List <Transaction>(); foreach (var item in unspent) { ExplorerResponse txResponse = explorer.GetTransaction(item.txid); RawFormat format = RawFormat.Satoshi; var tx = Transaction.Parse(txResponse.data, format, Network.TestNet); transactions.Add(tx); } //Create send transaction. //get redeem script var redeemScript = multi.GetRedeemScript(2, keys.ToArray()); Transaction received = transactions[0]; ScriptCoin coin = received.Outputs.AsCoins().First().ToScriptCoin(redeemScript); //create transaction: BitcoinAddress destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(coin); builder.Send(destination, Money.Coins(1.299m)); builder.SendFees(Money.Coins(0.001m)); builder.SetChange(destination); //builder. var unsigned = builder.BuildTransaction(sign: false); var signedA = builder.AddCoins(coin).AddKeys(keys[0].PrivateKey).SignTransaction(unsigned); Transaction signedB = builder.AddCoins(coin).AddKeys(keys[1].PrivateKey).SignTransaction(signedA); Transaction fullySigned = builder.AddCoins(coin).CombineSignatures(signedA, signedB); Console.WriteLine(fullySigned.ToHex()); Console.ReadLine(); }
public void NewCreateBasicSwap(uint path, params string[] seed) { List <ExtKey> keys = new List <ExtKey>(); Segwit segwit = new Segwit(NBitcoin.Network.TestNet); for (int i = 0; i < seed.Length; i++) { var key = GetKey(path, seed[i]); //var address = segwit.GetP2SHAddress(key); keys.Add(key); //Console.WriteLine(address.ToString()); } NBitcoin.Network _Network = NBitcoin.Network.TestNet; MultiSig multi = new MultiSig(NBitcoin.Network.TestNet); List <PubKey> pubKeys = new List <PubKey>(); for (int i = 0; i < keys.Count; i++) { pubKeys.Add(keys[i].PrivateKey.PubKey); } Script pubKeyScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKeys.ToArray()); BitcoinAddress address = pubKeyScript.WitHash.GetAddress(_Network); BitcoinScriptAddress p2sh = address.GetScriptAddress(); Console.WriteLine("Send money here: " + p2sh.ToString()); REST.BlockExplorer explorer = new REST.BlockExplorer("https://testnet.blockexplorer.com/"); var response = explorer.GetUnspent(p2sh.ToString()); List <ExplorerUnspent> unspent = response.Convert <List <ExplorerUnspent> >(); List <Transaction> transactions = new List <Transaction>(); foreach (var item in unspent) { ExplorerResponse txResponse = explorer.GetTransaction(item.txid); RawFormat format = RawFormat.Satoshi; var tx = Transaction.Parse(txResponse.data, format, Network.TestNet); transactions.Add(tx); } //Create send transaction. //get redeem script //var redeemScript = PayToMultiSigTemplate.Instance.GenerateScriptPubKey(2, pubKeys.ToArray());// multi.GetRedeemScript(2, keys.ToArray()); Transaction received = transactions[0]; ScriptCoin coin = received.Outputs.AsCoins().First().ToScriptCoin(pubKeyScript.WitHash.ScriptPubKey.Hash.ScriptPubKey); //create transaction: BitcoinAddress destination = BitcoinAddress.Create("2N8hwP1WmJrFF5QWABn38y63uYLhnJYJYTF"); //the faucet return address TransactionBuilder builder = new TransactionBuilder(); builder.AddCoins(coin); builder.Send(destination, Money.Coins(1.299m)); builder.SendFees(Money.Coins(0.001m)); builder.SetChange(destination); //builder. var unsigned = builder.BuildTransaction(sign: false); var signedA = builder.AddCoins(coin).AddKeys(keys[0].PrivateKey).SignTransaction(unsigned); Transaction signedB = builder.AddCoins(coin).AddKeys(keys[1].PrivateKey).SignTransaction(signedA); Transaction fullySigned = builder.AddCoins(coin).CombineSignatures(signedA, signedB); Console.WriteLine(fullySigned.ToHex()); Console.ReadLine(); }
public static string ToJsonString(this Transaction transaction, RawFormat rawFormat, Network network = null) { var formatter = network.GetFormatter(rawFormat); return(ToJsonString(transaction, formatter)); }
/// <summary> /// Saves the currently loaded image to a uncompressed or paletted Blp file. /// </summary> /// <param name="filename">The path to the output file.</param> /// <param name="format">Raw format. See <see cref="RawFormat"/></param> /// <param name="resizeMethod">Method for possibly required resizing. See <see cref="FixSize"/></param> /// <param name="hasMipmaps">Specifies whether mipmaps are generated or not.</param> /// <param name="samplingFactor">(Optional) Sampling factor 1-30. Lower values stand for higher quality to the disadvantage of speed. Does not apply to Raw3 encoding.</param> /// <param name="samplingFactor">(Optional) Specifies whether or not to use dithering. Does not apply to Raw3 encoding.</param> public void Save(string filename, RawFormat format, ResizeMethod resizeMethod, bool hasMipmaps, byte samplingFactor = 10, bool dither = false) { if (bmp != null) { FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Write); using (BinaryWriter bw = new BinaryWriter(fs)) { Blp2Header header = new Blp2Header(DataType.Uncompressed_DirectX, 0, AlphaDepth.Alpha8Bit, AlphaEncoding.RAW8BIT, (byte)(hasMipmaps ? 1 : 0), 0, 0); List <Bitmap> bmps = new List <Bitmap>(); // Add resized image to the output bmps.Add(FixSize(resizeMethod)); header.width = (uint)bmps[0].Width; header.height = (uint)bmps[0].Height; // Add mipmaps if (hasMipmaps) { bmps.AddRange(GenerateMipmaps(bmps[0])); } byte[] compImgData = { }; byte[] colorPalette = new byte[256 * 4]; if (format == RawFormat.Raw1) { // Uncompressed paletted image if (samplingFactor < 1 || samplingFactor > 30) { throw new ArgumentException("Sampling factor must be between 1 and 30."); } header.encoding = Encoding.RAW1; // Quantize colors to a 256 color palette NeuQuant quant = new NeuQuant(bmps[0]); quant.Quantize(); // Set color palette int j = 0; foreach (Color c in quant.Palette) { colorPalette[j] = c.R; colorPalette[j + 1] = c.G; colorPalette[j + 2] = c.B; colorPalette[j + 3] = 0; j += 4; } for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); byte[] alphaData = new byte[imgData.Length / 4]; // Index image colors int curOffset = compImgData.Length; Array.Resize(ref compImgData, compImgData.Length + imgData.Length / 4); for (j = 0; j < imgData.Length; j += 4) { Color c = Color.FromArgb(imgData[j], imgData[j + 1], imgData[j + 2]); compImgData[curOffset + j / 4] = (byte)quant.GetPaletteIndex(c); // Floyd-Steinberg dithering if (dither) { // Quantization error int diffErrR = c.R - quant.Palette[compImgData[curOffset + j / 4]].R; int diffErrG = c.G - quant.Palette[compImgData[curOffset + j / 4]].G; int diffErrB = c.B - quant.Palette[compImgData[curOffset + j / 4]].B; // Diffuse error, distribute to neighbour bytes as follows: // X P 7/16 // 3/16 5/16 1/16 if (bmpData.Stride - (j % bmpData.Stride) >= 8) { imgData[j + 4] = truncateByte(imgData[j + 4], (diffErrR * 7) >> 4); imgData[j + 5] = truncateByte(imgData[j + 5], (diffErrG * 7) >> 4); imgData[j + 6] = truncateByte(imgData[j + 6], (diffErrB * 7) >> 4); } else if (j + bmpData.Stride < imgData.Length) { imgData[j + bmpData.Stride] = truncateByte(imgData[j + bmpData.Stride], (diffErrR * 5) >> 4); imgData[j + bmpData.Stride + 1] = truncateByte(imgData[j + bmpData.Stride + 1], (diffErrG * 5) >> 4); imgData[j + bmpData.Stride + 2] = truncateByte(imgData[j + bmpData.Stride + 2], (diffErrB * 5) >> 4); if (bmpData.Stride - (j % bmpData.Stride) >= 8) { imgData[j + bmpData.Stride + 4] = truncateByte(imgData[j + bmpData.Stride + 4], (diffErrR * 1) >> 4); imgData[j + bmpData.Stride + 5] = truncateByte(imgData[j + bmpData.Stride + 5], (diffErrG * 1) >> 4); imgData[j + bmpData.Stride + 6] = truncateByte(imgData[j + bmpData.Stride + 6], (diffErrB * 1) >> 4); } else if (j % bmpData.Stride >= 4) { imgData[j + bmpData.Stride - 4] = truncateByte(imgData[j + bmpData.Stride - 4], (diffErrR * 3) >> 4); imgData[j + bmpData.Stride - 3] = truncateByte(imgData[j + bmpData.Stride - 3], (diffErrG * 3) >> 4); imgData[j + bmpData.Stride - 2] = truncateByte(imgData[j + bmpData.Stride - 2], (diffErrB * 3) >> 4); } } } // Fill alpha array (8 bit) alphaData[j / 4] = imgData[j + 3]; } // Append alpha channels Array.Resize(ref compImgData, compImgData.Length + alphaData.Length); alphaData.CopyTo(compImgData, compImgData.Length - alphaData.Length); // Image data locations header.offsets[i] = (uint)(curOffset + Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)(compImgData.Length - ((i > 0) ? header.offsets[i - 1] : 0)); } } else if (format == RawFormat.Raw3) { // Uncompressed 32bpp image header.encoding = Encoding.RAW3; for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); Array.Resize(ref compImgData, compImgData.Length + imgData.Length); imgData.CopyTo(compImgData, compImgData.Length - imgData.Length); // Image data locations header.offsets[i] = (i > 0) ? header.offsets[i - 1] + header.lengths[i - 1] : (uint)(Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)imgData.Length; } } bw.Write(header.GetBytes()); bw.Write(colorPalette); bw.Write(compImgData); } } else { throw new BlpConversionException("No image data to convert."); } }
public string ToString(RawFormat rawFormat, Network network = null) { var formatter = GetFormatter(rawFormat, network); return ToString(formatter); }
/// <summary> /// Saves the currently loaded image to a uncompressed or paletted Blp file. /// </summary> /// <param name="filename">The path to the output file.</param> /// <param name="format">Raw format. See <see cref="RawFormat"/></param> /// <param name="resizeMethod">Method for possibly required resizing. See <see cref="FixSize"/></param> /// <param name="hasMipmaps">Specifies whether mipmaps are generated or not.</param> /// <param name="samplingFactor">(Optional) Sampling factor 1-30. Lower values stand for higher quality to the disadvantage of speed. Does not apply to Raw3 encoding.</param> /// <param name="samplingFactor">(Optional) Specifies whether or not to use dithering. Does not apply to Raw3 encoding.</param> public void Save(string filename, RawFormat format, ResizeMethod resizeMethod, bool hasMipmaps, byte samplingFactor = 10, bool dither = false) { if (bmp != null) { FileStream fs = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Write); using (BinaryWriter bw = new BinaryWriter(fs)) { Blp2Header header = new Blp2Header(DataType.Uncompressed_DirectX, 0, AlphaDepth.Alpha8Bit, AlphaEncoding.RAW8BIT, (byte)(hasMipmaps ? 1 : 0), 0, 0); List<Bitmap> bmps = new List<Bitmap>(); // Add resized image to the output bmps.Add(FixSize(resizeMethod)); header.width = (uint)bmps[0].Width; header.height = (uint)bmps[0].Height; // Add mipmaps if (hasMipmaps) bmps.AddRange(GenerateMipmaps(bmps[0])); byte[] compImgData = { }; byte[] colorPalette = new byte[256 * 4]; if (format == RawFormat.Raw1) { // Uncompressed paletted image if (samplingFactor < 1 || samplingFactor > 30) throw new ArgumentException("Sampling factor must be between 1 and 30."); header.encoding = Encoding.RAW1; // Quantize colors to a 256 color palette NeuQuant quant = new NeuQuant(bmps[0]); quant.Quantize(); // Set color palette int j = 0; foreach (Color c in quant.Palette) { colorPalette[j] = c.R; colorPalette[j + 1] = c.G; colorPalette[j + 2] = c.B; colorPalette[j + 3] = 0; j += 4; } for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); byte[] alphaData = new byte[imgData.Length / 4]; // Index image colors int curOffset = compImgData.Length; Array.Resize(ref compImgData, compImgData.Length + imgData.Length / 4); for (j = 0; j < imgData.Length; j += 4) { Color c = Color.FromArgb(imgData[j], imgData[j + 1], imgData[j + 2]); compImgData[curOffset + j / 4] = (byte)quant.GetPaletteIndex(c); // Floyd-Steinberg dithering if (dither) { // Quantization error int diffErrR = c.R - quant.Palette[compImgData[curOffset + j / 4]].R; int diffErrG = c.G - quant.Palette[compImgData[curOffset + j / 4]].G; int diffErrB = c.B - quant.Palette[compImgData[curOffset + j / 4]].B; // Diffuse error, distribute to neighbour bytes as follows: // X P 7/16 // 3/16 5/16 1/16 if (bmpData.Stride - (j % bmpData.Stride) >= 8) { imgData[j + 4] = truncateByte(imgData[j + 4], (diffErrR * 7) >> 4); imgData[j + 5] = truncateByte(imgData[j + 5], (diffErrG * 7) >> 4); imgData[j + 6] = truncateByte(imgData[j + 6], (diffErrB * 7) >> 4); } else if (j + bmpData.Stride < imgData.Length) { imgData[j + bmpData.Stride] = truncateByte(imgData[j + bmpData.Stride], (diffErrR * 5) >> 4); imgData[j + bmpData.Stride + 1] = truncateByte(imgData[j + bmpData.Stride + 1], (diffErrG * 5) >> 4); imgData[j + bmpData.Stride + 2] = truncateByte(imgData[j + bmpData.Stride + 2], (diffErrB * 5) >> 4); if (bmpData.Stride - (j % bmpData.Stride) >= 8) { imgData[j + bmpData.Stride + 4] = truncateByte(imgData[j + bmpData.Stride + 4], (diffErrR * 1) >> 4); imgData[j + bmpData.Stride + 5] = truncateByte(imgData[j + bmpData.Stride + 5], (diffErrG * 1) >> 4); imgData[j + bmpData.Stride + 6] = truncateByte(imgData[j + bmpData.Stride + 6], (diffErrB * 1) >> 4); } else if (j % bmpData.Stride >= 4) { imgData[j + bmpData.Stride - 4] = truncateByte(imgData[j + bmpData.Stride - 4], (diffErrR * 3) >> 4); imgData[j + bmpData.Stride - 3] = truncateByte(imgData[j + bmpData.Stride - 3], (diffErrG * 3) >> 4); imgData[j + bmpData.Stride - 2] = truncateByte(imgData[j + bmpData.Stride - 2], (diffErrB * 3) >> 4); } } } // Fill alpha array (8 bit) alphaData[j / 4] = imgData[j + 3]; } // Append alpha channels Array.Resize(ref compImgData, compImgData.Length + alphaData.Length); alphaData.CopyTo(compImgData, compImgData.Length - alphaData.Length); // Image data locations header.offsets[i] = (uint)(curOffset + Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)(compImgData.Length - ((i > 0) ? header.offsets[i - 1] : 0)); } } else if (format == RawFormat.Raw3) { // Uncompressed 32bpp image header.encoding = Encoding.RAW3; for (int i = 0; i < bmps.Count; i++) { // Copy raw image data to byte array BitmapData bmpData = bmps[i].LockBits(new Rectangle(0, 0, bmps[i].Width, bmps[i].Height), ImageLockMode.ReadOnly, bmps[i].PixelFormat); byte[] imgData = new byte[bmpData.Stride * bmps[i].Height]; Marshal.Copy(bmpData.Scan0, imgData, 0, bmpData.Stride * bmps[i].Height); bmps[i].UnlockBits(bmpData); Array.Resize(ref compImgData, compImgData.Length + imgData.Length); imgData.CopyTo(compImgData, compImgData.Length - imgData.Length); // Image data locations header.offsets[i] = (i > 0) ? header.offsets[i - 1] + header.lengths[i - 1] : (uint)(Marshal.SizeOf(typeof(Blp2Header)) + colorPalette.Length); header.lengths[i] = (uint)imgData.Length; } } bw.Write(header.GetBytes()); bw.Write(colorPalette); bw.Write(compImgData); } } else { throw new BlpConversionException("No image data to convert."); } }