internal static void CompressBC2Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting) { // Compress Alpha if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible. for (int i = 0; i < 8; i++) destination[destPosition + i] = 0xFF; } else { int position = sourcePosition + 3; // Only want to read alphas for (int i = 0; i < 8; i += 2) { destination[destPosition + i] = (byte)((imgData[position] & 0xF0) | (imgData[position + 4] >> 4)); destination[destPosition + i + 1] = (byte)((imgData[position + 8] & 0xF0) | (imgData[position + 12] >> 4)); position += sourceLineLength; } } // Compress Colour CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting); }
public AlphaPanel(AlphaSettings settings) { InitializeComponent(); string stretchtttxt = "Using stretch stretches alpha values to the range [0,255].\n" + "The given minimum value will be mapped onto 0 (transparent),\n" + "the given maximum value onto 255 (opaque)."; this.stretch_tt.SetToolTip(this.stretch_group, stretchtttxt); this.stretch_tt.SetToolTip(this.strech_enable, stretchtttxt); this.stretch_tt.SetToolTip(this.min_box, stretchtttxt); this.stretch_tt.SetToolTip(this.min_label, stretchtttxt); this.stretch_tt.SetToolTip(this.max_box, stretchtttxt); this.stretch_tt.SetToolTip(this.max_label, stretchtttxt); this.settings = settings; // set location switch (settings.Location) { case AlphaLocation.START: start_radio.Checked = true; break; case AlphaLocation.END: end_radio.Checked = true; break; } // set stretch this.strech_enable.Checked = settings.Stretch; this.min_box.Text = settings.Minimum + ""; this.max_box.Text = settings.Maximum + ""; // set ignore this.ignore_check.Checked = settings.IgnoreAlpha; }
/// <summary> /// Saves fully formatted image in specified format to byte array. /// </summary> /// <param name="destFormatDetails">Details about destination format.</param> /// <param name="GenerateMips">Determines how mipmaps are handled during saving.</param> /// <param name="desiredMaxDimension">Maximum size for saved image. Resizes if required, but uses mipmaps if available.</param> /// <param name="mipToSave">Index of mipmap to save directly.</param> /// <param name="removeAlpha">True = Alpha removed. False = Uses threshold value and alpha values to mask RGB FOR DXT1, otherwise completely removed.</param> /// <returns></returns> public byte[] Save(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension = 0, int mipToSave = 0, bool removeAlpha = true) { if (destFormatDetails.Format == ImageEngineFormat.Unknown) { throw new InvalidOperationException("Save format cannot be 'Unknown'"); } AlphaSettings alphaSetting = AlphaSettings.KeepAlpha; if (removeAlpha) { alphaSetting = AlphaSettings.RemoveAlphaChannel; } else if (destFormatDetails.Format == ImageEngineFormat.DDS_DXT2 || destFormatDetails.Format == ImageEngineFormat.DDS_DXT4) { alphaSetting = AlphaSettings.Premultiply; } // If same format and stuff, can just return original data, or chunks of it. if (destFormatDetails.Format == Format) { return(AttemptSaveUsingOriginalData(destFormatDetails, GenerateMips, desiredMaxDimension, mipToSave, alphaSetting)); } else { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } }
// ATI2 3Dc internal static void CompressBC5Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting) { // Green: Channel 1. Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 1, false); // Red: Channel 2, 8 destination offset to be after Green. Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, 2, false); }
/// <summary> /// Generates alpha server settings /// </summary> /// <returns>Settings object</returns> public static AlphaSettings GetAlphaSettings() { var settings = new AlphaSettings(); SetCommonSettings(settings, TestEnvironment.AlphaKeyPair.SecretSeed); settings.ConnectionString = "mongodb://localhost:27001/alphaDBTest?replicaSet=centaurus"; settings.Build(); return(settings); }
public void Setup() { EnvironmentHelper.SetTestEnvironmentVariable(); var settings = new AlphaSettings { CWD = "AppData" }; Global.Setup(settings, new MongoStorage()).Wait(); }
public void Setup() { EnvironmentHelper.SetTestEnvironmentVariable(); var settings = new AlphaSettings { CWD = "AppData" }; var stellarProvider = new MockStellarDataProvider(settings.NetworkPassphrase, settings.HorizonUrl); context = new AlphaContext(settings, new MongoStorage(), stellarProvider); context.Init().Wait(); }
static int WriteCompressedMipMap(byte[] destination, int mipOffset, MipMap mipmap, int blockSize, Action <byte[], int, int, byte[], int, AlphaSettings, ImageFormats.ImageEngineFormatDetails> compressor, AlphaSettings alphaSetting) { if (mipmap.Width < 4 || mipmap.Height < 4) { return(-1); } int destinationTexelCount = mipmap.Width * mipmap.Height / 16; int sourceLineLength = mipmap.Width * 4 * mipmap.LoadedFormatDetails.ComponentSize; int numTexelsInLine = mipmap.Width / 4; var mipWriter = new Action <int>(texelIndex => { // Since this is the top corner of the first texel in a line, skip 4 pixel rows (texel = 4x4 pixels) and the number of rows down the bitmap we are already. int sourceLineOffset = sourceLineLength * 4 * (texelIndex / numTexelsInLine); // Length in bytes x 3 lines x texel line index (how many texel sized lines down the image are we). Index / width will truncate, so for the first texel line, it'll be < 0. For the second texel line, it'll be < 1 and > 0. int sourceTopLeftCorner = ((texelIndex % numTexelsInLine) * 16) * mipmap.LoadedFormatDetails.ComponentSize + sourceLineOffset; // *16 since its 4 pixels with 4 channels each. Index % numTexels will effectively reset each line. compressor(mipmap.Pixels, sourceTopLeftCorner, sourceLineLength, destination, mipOffset + texelIndex * blockSize, alphaSetting, mipmap.LoadedFormatDetails); }); // Choose an acceleration method. if (ImageEngine.EnableThreading) { Parallel.For(0, destinationTexelCount, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, (mip, loopState) => { if (ImageEngine.IsCancellationRequested) { loopState.Stop(); } mipWriter(mip); }); } else { for (int i = 0; i < destinationTexelCount; i++) { if (ImageEngine.IsCancellationRequested) { break; } mipWriter(i); } } return(mipOffset + destinationTexelCount * blockSize); }
internal static void CompressBC3Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting) { // Compress Alpha if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible. for (int i = 0; i < 8; i++) destination[destPosition + i] = 0xFF; } else Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 3, false); // Compress Colour CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting); }
private AlphaSettings GetAlphaSettings() { var kp = KeyPair.Random(); var alphaSettings = new AlphaSettings { AlphaPort = 5000, HorizonUrl = HorizonUrl, NetworkPassphrase = NetworkPassphrase, Secret = kp.SecretSeed, SyncBatchSize = 500 }; alphaSettings.Build(); return(alphaSettings); }
private void SetupCertificate(AlphaSettings alphaSettings) { if (alphaSettings.TlsCertificatePath == null) { return; } if (!File.Exists(alphaSettings.TlsCertificatePath)) { throw new FileNotFoundException($"Failed to find a certificate \"{alphaSettings.TlsCertificatePath}\""); } UpdateCertificate(alphaSettings.TlsCertificatePath, alphaSettings.TlsCertificatePrivateKeyPath); ObserveCertificateChange(alphaSettings.TlsCertificatePath, alphaSettings.TlsCertificatePrivateKeyPath); }
public IHost CreateHost(AlphaSettings settings) { SetupCertificate(settings); return(Host.CreateDefaultBuilder() .ConfigureLogging(logging => { logging.ClearProviders(); logging.AddConsole(); if (settings.Verbose) { logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Trace); } else if (settings.Silent) { logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Error); } else { logging.SetMinimumLevel(Microsoft.Extensions.Logging.LogLevel.Information); } }) .UseNLog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureServices(s => s.AddSingleton(Context)) .UseStartup <HostStartup>() .UseKestrel(options => { if (Certificate != null) { options.ListenAnyIP(settings.AlphaPort, listenOptions => { var httpsOptions = new HttpsConnectionAdapterOptions(); httpsOptions.ServerCertificateSelector = (context, path) => Certificate; listenOptions.UseHttps(httpsOptions); }); } else { options.ListenAnyIP(settings.AlphaPort); } }); }).Build()); }
static void Client() { MessageHandlers.Init(); var settings = new AlphaSettings { HorizonUrl = "https://horizon-testnet.stellar.org", NetworkPassphrase = "Test SDF Network ; September 2015" }; Global.Init(settings, new MongoStorage()); UserWebSocketConnection ws = new UserWebSocketConnection(null); ws.EstablishConnection().Wait(); Console.WriteLine("Type 'q' to close..."); Console.WriteLine("Place order format: po {order-direction} {amount (will be multiplied by 10 000 000)} {price}"); Console.WriteLine("Example: po 0 1 2"); while (true) { try { var line = Console.ReadLine(); if (line == "q") { break; } if (line.IndexOf("po") == 0) { var poArgs = line.Split(' ', StringSplitOptions.RemoveEmptyEntries); var res = ws.PlaceOrder(int.Parse(poArgs[1]), long.Parse(poArgs[2]) * 10_000_000, double.Parse(poArgs[3])).Result; Console.WriteLine(res.Status.ToString()); } } catch (Exception exc) { Console.WriteLine(exc); } } }
public void Setup() { var dbName = "testDB"; var replicaSet = "centaurusTest"; var dbPort = 27001; MongoDBServerHelper.RunMongoDBServers(new int[] { dbPort }, replicaSet); var extensionsPath = ExtensionConfigGenerator.Generate(dbPort, dbName, replicaSet, banPeriod, boostFactor); var settings = new AlphaSettings(); settings.ExtensionsConfigFilePath = Path.GetFullPath(extensionsPath); settings.ConnectionString = $"mongodb://localhost:{dbPort}/{dbName}?replicaSet={replicaSet}"; settings.HorizonUrl = "https://horizon-testnet.stellar.org"; settings.NetworkPassphrase = "Test SDF Network ; September 2015"; settings.CWD = "AppData"; settings.Secret = TestEnvironment.AlphaKeyPair.SecretSeed; settings.Build(); GlobalInitHelper.Setup(GlobalInitHelper.GetPredefinedClients(), GlobalInitHelper.GetPredefinedAuditors(), settings, new MongoStorage()); }
public void Setup() { EnvironmentHelper.SetTestEnvironmentVariable(); var settings = new AlphaSettings { HorizonUrl = "https://horizon-testnet.stellar.org", NetworkPassphrase = "Test SDF Network ; September 2015", CWD = "AppData" }; var stellarProvider = new MockStellarDataProvider(settings.NetworkPassphrase, settings.HorizonUrl); context = new AlphaContext(settings, new MockStorage(), stellarProvider); context.Init().Wait(); var requestsLimit = new RequestRateLimits(); account1 = new AccountWrapper(new Models.Account { Id = 1, Pubkey = new RawPubKey() { Data = KeyPair.Random().PublicKey }, Balances = new List <Balance>() }, requestsLimit); account1.Account.CreateBalance(0); account1.Account.GetBalance(0).UpdateBalance(10000000000); account1.Account.CreateBalance(1); account1.Account.GetBalance(1).UpdateBalance(10000000000); account2 = new AccountWrapper(new Models.Account { Id = 2, Pubkey = new RawPubKey() { Data = KeyPair.Random().PublicKey }, Balances = new List <Balance>() }, requestsLimit); account2.Account.CreateBalance(0); account2.Account.GetBalance(0).UpdateBalance(10000000000); account2.Account.CreateBalance(1); account2.Account.GetBalance(1).UpdateBalance(10000000000); context.Setup(new Snapshot { Accounts = new List <AccountWrapper> { account1, account2 }, Apex = 0, TxCursor = 1, Orders = new List <Order>(), Settings = new ConstellationSettings { Vault = KeyPair.Random().PublicKey, Assets = new List <AssetSettings> { new AssetSettings { Id = 1, Code = "X", Issuer = new RawPubKey() } }, RequestRateLimits = new RequestRateLimits { HourLimit = 1000, MinuteLimit = 100 } }, }).Wait(); }
// ATI1 internal static void CompressBC4Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting) { Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 2, false); }
internal static byte[] Save(List<MipMap> mipMaps, ImageEngineFormat saveFormat, AlphaSettings alphaSetting, List<uint> customMasks = null) { // Set compressor for Block Compressed textures Action<byte[], int, int, byte[], int, AlphaSettings> compressor = null; bool needCheckSize = saveFormat.ToString().Contains("DXT") || saveFormat.ToString().Contains("ATI"); switch (saveFormat) { case ImageEngineFormat.DDS_ATI1: compressor = DDS_Encoders.CompressBC4Block; break; case ImageEngineFormat.DDS_ATI2_3Dc: compressor = DDS_Encoders.CompressBC5Block; break; case ImageEngineFormat.DDS_DX10: Debugger.Break(); break; // TODO: NOT SUPPORTED YET. DX10 case ImageEngineFormat.DDS_DXT1: compressor = DDS_Encoders.CompressBC1Block; break; case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT3: compressor = DDS_Encoders.CompressBC2Block; break; case ImageEngineFormat.DDS_DXT4: case ImageEngineFormat.DDS_DXT5: compressor = DDS_Encoders.CompressBC3Block; break; } int height = mipMaps[0].Height; int width = mipMaps[0].Width; if (needCheckSize && !CheckSize_DXT(width, height)) throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}."); int fullSize = GetCompressedSizeOfImage(mipMaps.Count, saveFormat, width, height); // +1 to get the full size, not just the offset of the last mip. //int fullSize = GetMipOffset(mipMaps.Count + 1, saveFormat, mipMaps[0].Width, mipMaps[0].Height); byte[] destination = new byte[fullSize]; // Create header and write to destination DDS_Header header = new DDS_Header(mipMaps.Count, height, width, saveFormat, customMasks); header.WriteToArray(destination, 0); int blockSize = ImageFormats.GetBlockSize(saveFormat); if (ImageFormats.IsBlockCompressed(saveFormat)) { int mipOffset = 128; foreach (MipMap mipmap in mipMaps) mipOffset = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting); } else { // UNCOMPRESSED var action = new Action<int>(mipIndex => { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Remove alpha by setting AMask = 0 var ddspf = header.ddspf; ddspf.dwABitMask = 0; header.ddspf = ddspf; } // Get MipOffset int offset = GetMipOffset(mipIndex, saveFormat, width, height); WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], saveFormat, header.ddspf); }); if (ImageEngine.EnableThreading) Parallel.For(0, mipMaps.Count, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, action); else for (int i = 0; i < mipMaps.Count; i++) action(i); } return destination; }
public void Setup() { EnvironmentHelper.SetTestEnvironmentVariable(); var settings = new AlphaSettings { HorizonUrl = "https://horizon-testnet.stellar.org", NetworkPassphrase = "Test SDF Network ; September 2015", CWD = "AppData" }; Global.Setup(settings, new MockStorage()).Wait(); var account1 = new Models.Account() { Id = 1, Pubkey = new RawPubKey() { Data = KeyPair.Random().PublicKey }, Balances = new List <Balance>() }; account1.CreateBalance(0); account1.GetBalance(0).UpdateBalance(10000000000); account1.CreateBalance(1); account1.GetBalance(1).UpdateBalance(10000000000); var account2 = new Models.Account() { Id = 2, Pubkey = new RawPubKey() { Data = KeyPair.Random().PublicKey }, Balances = new List <Balance>() }; account2.CreateBalance(0); account2.GetBalance(0).UpdateBalance(10000000000); account2.CreateBalance(1); account2.GetBalance(1).UpdateBalance(10000000000); Global.Setup(new Snapshot { Accounts = new List <Models.Account> { account1, account2 }, Apex = 0, TxCursor = 1, Orders = new List <Order>(), Settings = new ConstellationSettings { Vault = KeyPair.Random().PublicKey, Assets = new List <AssetSettings> { new AssetSettings { Id = 1, Code = "X", Issuer = new RawPubKey() } }, RequestRateLimits = new RequestRateLimits { HourLimit = 1000, MinuteLimit = 100 } }, }).Wait(); this.account1 = Global.AccountStorage.GetAccount(account1.Id); this.account2 = Global.AccountStorage.GetAccount(account2.Id); }
// ATI1 internal static void CompressBC4Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, DDSFormatDetails formatDetails) { Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 2, false, formatDetails); }
internal static void CompressBC3Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails) { // Compress Alpha if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible. for (int i = 0; i < 8; i++) { destination[destPosition + i] = 0xFF; } } else { Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 3, false, formatDetails); } // Compress Colour CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails); }
internal static void CompressRGBTexel(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, bool isDXT1, double alphaRef, AlphaSettings alphaSetting, DDSFormatDetails formatDetails) { int uSteps = 4; bool premultiply = alphaSetting == AlphaSettings.Premultiply; // Read texel RGBColor[] sourceTexel = new RGBColor[16]; int position = sourcePosition; int count = 0; for (int i = 1; i <= 4; i++) { for (int j = 0; j < 4; j++) { sourceTexel[count++] = ReadColorFromTexel(imgData, position, premultiply, formatDetails); position += 4 * formatDetails.ComponentSize; } position = sourcePosition + sourceLineLength * i; } // TODO replace RGBColor with a SIMD vector for speed. Test difference between vector4 and vector<T>, might not be better. // Determine if texel is fully and entirely transparent. If so, can set to white and continue. if (isDXT1) { uSteps = CheckDXT1TexelFullTransparency(sourceTexel, destination, destPosition, alphaRef); if (uSteps == -1) { return; } } RGBColor[] Color = new RGBColor[16]; // Some kind of color adjustment. Not sure what it does, especially if it wasn't dithering... DoColorFixErrorCorrection(Color, sourceTexel); // Palette colors RGBColor ColorA, ColorB, ColorC, ColorD; ColorA = new RGBColor(); ColorB = new RGBColor(); ColorC = new RGBColor(); ColorD = new RGBColor(); // OPTIMISER RGBColor[] minmax = OptimiseRGB(Color, uSteps); ColorA = minmax[0]; ColorB = minmax[1]; // Create interstitial colors? ColorC.r = ColorA.r * LuminanceInv.r; ColorC.g = ColorA.g * LuminanceInv.g; ColorC.b = ColorA.b * LuminanceInv.b; ColorD.r = ColorB.r * LuminanceInv.r; ColorD.g = ColorB.g * LuminanceInv.g; ColorD.b = ColorB.b * LuminanceInv.b; // Yeah...dunno uint wColorA = Encode565(ColorC); uint wColorB = Encode565(ColorD); // Min max are equal - only interpolate 4 interstitial colors if (uSteps == 4 && wColorA == wColorB) { var c2 = BitConverter.GetBytes(wColorA); var c1 = BitConverter.GetBytes(wColorB); ///////////////////// MIN MAX destination[destPosition] = c2[0]; destination[destPosition + 1] = c2[1]; destination[destPosition + 2] = c1[0]; destination[destPosition + 3] = c1[1]; return; } // Interpolate 6 colors or something ColorC = Decode565(wColorA); ColorD = Decode565(wColorB); ColorA.r = ColorC.r * Luminance.r; ColorA.g = ColorC.g * Luminance.g; ColorA.b = ColorC.b * Luminance.b; ColorB.r = ColorD.r * Luminance.r; ColorB.g = ColorD.g * Luminance.g; ColorB.b = ColorD.b * Luminance.b; var step = DoSomethingWithPalette(uSteps, wColorA, wColorB, ColorA, ColorB); // Calculating color direction apparently RGBColor Dir = new RGBColor() { r = step[1].r - step[0].r, g = step[1].g - step[0].g, b = step[1].b - step[0].b }; float fscale = (wColorA != wColorB) ? ((uSteps - 1) / (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b)) : 0.0f; Dir.r *= fscale; Dir.g *= fscale; Dir.b *= fscale; // Encoding colors apparently uint dw = DoOtherColorFixErrorCorrection(sourceTexel, uSteps, alphaRef, step, Dir); uint Min = (uSteps == 3) == (wColorA <= wColorB) ? wColorA : wColorB; uint Max = (uSteps == 3) == (wColorA <= wColorB) ? wColorB : wColorA; var color1 = BitConverter.GetBytes(Min); var color2 = BitConverter.GetBytes(Max); destination[destPosition] = color1[0]; destination[destPosition + 1] = color1[1]; destination[destPosition + 2] = color2[0]; destination[destPosition + 3] = color2[1]; var indicies = BitConverter.GetBytes(dw); destination[destPosition + 4] = indicies[0]; destination[destPosition + 5] = indicies[1]; destination[destPosition + 6] = indicies[2]; destination[destPosition + 7] = indicies[3]; }
/// <summary> /// Not exactly sure what this does or why. /// </summary> static void DoColourFixErrorCorrection(RGBColour[] Colour, byte[] imgData, int sourcePosition, int sourceLineLength, AlphaSettings alphaSetting) { RGBColour[] Error = new RGBColour[16]; for (int i = 0; i < 4; i++) { int position = sourcePosition + sourceLineLength * i; for (int j = 0; j < 4; j++) { int index = (i << 2) + j; RGBColour current = ReadColourFromTexel(imgData, position, alphaSetting == AlphaSettings.Premultiply); if (true) // Dither { // Adjust for accumulated error // This works by figuring out the error between the current pixel colour and the adjusted colour? Dunno what the adjustment is. Looks like a 5:6:5 range adaptation // Then, this error is distributed across the "next" few pixels and not the previous. current.r += Error[index].r; current.g += Error[index].g; current.b += Error[index].b; } // 5:6:5 range adaptation? Colour[index].r = (int)(current.r * 31f + .5f) * (1f / 31f); Colour[index].g = (int)(current.g * 63f + .5f) * (1f / 63f); Colour[index].b = (int)(current.b * 31f + .5f) * (1f / 31f); DoSomeDithering(current, index, Colour, index, Error); Colour[index].r *= Luminance.r; Colour[index].g *= Luminance.g; Colour[index].b *= Luminance.b; position += 4; } } }
internal static void CompressRGBTexel(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, bool isDXT1, double alphaRef, AlphaSettings alphaSetting) { int uSteps = 4; // Determine if texel is fully and entirely transparent. If so, can set to white and continue. if (isDXT1) { uSteps = CheckDXT1TexelFullTransparency(imgData, sourcePosition, sourceLineLength, destination, destPosition, alphaSetting, alphaRef); if (uSteps == -1) return; } RGBColour[] Colour = new RGBColour[16]; // Some kind of colour adjustment. Not sure what it does, especially if it wasn't dithering... DoColourFixErrorCorrection(Colour, imgData, sourcePosition, sourceLineLength, alphaSetting); // Palette colours RGBColour ColourA, ColourB, ColourC, ColourD; ColourA = new RGBColour(); ColourB = new RGBColour(); ColourC = new RGBColour(); ColourD = new RGBColour(); // OPTIMISER RGBColour[] minmax = OptimiseRGB(Colour, uSteps); ColourA = minmax[0]; ColourB = minmax[1]; // Create interstitial colours? ColourC.r = ColourA.r * LuminanceInv.r; ColourC.g = ColourA.g * LuminanceInv.g; ColourC.b = ColourA.b * LuminanceInv.b; ColourD.r = ColourB.r * LuminanceInv.r; ColourD.g = ColourB.g * LuminanceInv.g; ColourD.b = ColourB.b * LuminanceInv.b; // Yeah...dunno uint wColourA = Encode565(ColourC); uint wColourB = Encode565(ColourD); // Min max are equal - only interpolate 4 interstitial colours if (uSteps == 4 && wColourA == wColourB) { var c2 = BitConverter.GetBytes(wColourA); var c1 = BitConverter.GetBytes(wColourB); ///////////////////// MIN MAX destination[destPosition] = c2[0]; destination[destPosition + 1] = c2[1]; destination[destPosition + 2] = c1[0]; destination[destPosition + 3] = c1[1]; return; } // Interpolate 6 colours or something ColourC = Decode565(wColourA); ColourD = Decode565(wColourB); ColourA.r = ColourC.r * Luminance.r; ColourA.g = ColourC.g * Luminance.g; ColourA.b = ColourC.b * Luminance.b; ColourB.r = ColourD.r * Luminance.r; ColourB.g = ColourD.g * Luminance.g; ColourB.b = ColourD.b * Luminance.b; var step = DoSomethingWithPalette(uSteps, wColourA, wColourB, ColourA, ColourB); // Calculating colour direction apparently RGBColour Dir = new RGBColour() { r = step[1].r - step[0].r, g = step[1].g - step[0].g, b = step[1].b - step[0].b }; float fscale = (wColourA != wColourB) ? ((uSteps - 1) / (Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b)) : 0.0f; Dir.r *= fscale; Dir.g *= fscale; Dir.b *= fscale; // Encoding colours apparently uint dw = DoOtherColourFixErrorCorrection(imgData, sourcePosition, sourceLineLength, uSteps, alphaRef, alphaSetting, step, Dir); uint Min = (uSteps == 3) == (wColourA <= wColourB) ? wColourA : wColourB; uint Max = (uSteps == 3) == (wColourA <= wColourB) ? wColourB : wColourA; var colour1 = BitConverter.GetBytes(Min); var colour2 = BitConverter.GetBytes(Max); destination[destPosition] = colour1[0]; destination[destPosition + 1] = colour1[1]; destination[destPosition + 2] = colour2[0]; destination[destPosition + 3] = colour2[1]; var indicies = BitConverter.GetBytes(dw); destination[destPosition + 4] = indicies[0]; destination[destPosition + 5] = indicies[1]; destination[destPosition + 6] = indicies[2]; destination[destPosition + 7] = indicies[3]; }
internal static void CompressBC7Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails) { BC7.CompressBC7Block(imgData, sourcePosition, sourceLineLength, destination, destPosition); }
internal static void CompressBC1Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails) { CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition, true, (alphaSetting == AlphaSettings.RemoveAlphaChannel ? 0 : DXT1AlphaThreshold), alphaSetting, formatDetails); }
internal static byte[] Save(List <MipMap> mipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, AlphaSettings alphaSetting) { // Set compressor for Block Compressed textures Action <byte[], int, int, byte[], int, AlphaSettings, ImageFormats.ImageEngineFormatDetails> compressor = destFormatDetails.BlockEncoder; bool needCheckSize = destFormatDetails.IsBlockCompressed; int height = mipMaps[0].Height; int width = mipMaps[0].Width; if (needCheckSize && !CheckSize_DXT(width, height)) { throw new InvalidOperationException($"DXT compression formats require dimensions to be multiples of 4. Got: {width}x{height}."); } // Create header and write to destination DDS_Header header = new DDS_Header(mipMaps.Count, height, width, destFormatDetails.Format, destFormatDetails.DX10Format); int headerLength = destFormatDetails.HeaderSize; int fullSize = GetCompressedSizeOfImage(mipMaps.Count, destFormatDetails, width, height); /*if (destFormatDetails.ComponentSize != 1) * fullSize += (fullSize - headerLength) * destFormatDetails.ComponentSize;*/// Size adjustment for destination to allow for different component sizes. byte[] destination = new byte[fullSize]; header.WriteToArray(destination, 0); int blockSize = destFormatDetails.BlockSize; if (destFormatDetails.IsBlockCompressed) { int mipOffset = headerLength; foreach (MipMap mipmap in mipMaps) { if (ImageEngine.IsCancellationRequested) { break; } var temp = WriteCompressedMipMap(destination, mipOffset, mipmap, blockSize, compressor, alphaSetting); if (temp != -1) // When dimensions too low. { mipOffset = temp; } } } else { // UNCOMPRESSED var action = new Action <int>(mipIndex => { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Remove alpha by setting AMask = 0 var ddspf = header.ddspf; ddspf.dwABitMask = 0; header.ddspf = ddspf; } // Get MipOffset int offset = GetMipOffset(mipIndex, destFormatDetails, width, height); WriteUncompressedMipMap(destination, offset, mipMaps[mipIndex], destFormatDetails, header.ddspf); }); if (ImageEngine.EnableThreading) { Parallel.For(0, mipMaps.Count, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, (mip, loopState) => { if (ImageEngine.IsCancellationRequested) { loopState.Stop(); } action(mip); }); } else { for (int i = 0; i < mipMaps.Count; i++) { if (ImageEngine.IsCancellationRequested) { break; } action(i); } } } return(ImageEngine.IsCancellationRequested ? null : destination); }
internal static void CompressBC2Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails) { // Compress Alpha if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // No alpha so fill with opaque alpha - has to be an alpha value, so make it so RGB is 100% visible. for (int i = 0; i < 8; i++) { destination[destPosition + i] = 0xFF; } } else { int position = sourcePosition + 3; // Only want to read alphas for (int i = 0; i < 8; i += 2) { destination[destPosition + i] = (byte)((imgData[position] & 0xF0) | (imgData[position + 4] >> 4)); destination[destPosition + i + 1] = (byte)((imgData[position + 8] & 0xF0) | (imgData[position + 12] >> 4)); position += sourceLineLength; } } // Compress Colour CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, false, 0f, alphaSetting, formatDetails); }
static int WriteCompressedMipMap(byte[] destination, int mipOffset, MipMap mipmap, int blockSize, Action<byte[], int, int, byte[], int, AlphaSettings> compressor, AlphaSettings alphaSetting) { int destinationTexelCount = mipmap.Width * mipmap.Height / 16; int sourceLineLength = mipmap.Width * 4; int numTexelsInLine = mipmap.Width / 4; var mipWriter = new Action<int>(texelIndex => { // Since this is the top corner of the first texel in a line, skip 4 pixel rows (texel = 4x4 pixels) and the number of rows down the bitmap we are already. int sourceLineOffset = sourceLineLength * 4 * (texelIndex / numTexelsInLine); // Length in bytes x 3 lines x texel line index (how many texel sized lines down the image are we). Index / width will truncate, so for the first texel line, it'll be < 0. For the second texel line, it'll be < 1 and > 0. int sourceTopLeftCorner = ((texelIndex % numTexelsInLine) * 16) + sourceLineOffset; // *16 since its 4 pixels with 4 channels each. Index % numTexels will effectively reset each line. compressor(mipmap.Pixels, sourceTopLeftCorner, sourceLineLength, destination, mipOffset + texelIndex * blockSize, alphaSetting); }); // Choose an acceleration method. if (ImageEngine.EnableGPUAcceleration) Debugger.Break(); else if (ImageEngine.EnableThreading) Parallel.For(0, destinationTexelCount, new ParallelOptions { MaxDegreeOfParallelism = ImageEngine.NumThreads }, mipWriter); else for (int i = 0; i < destinationTexelCount; i++) mipWriter(i); return mipOffset + destinationTexelCount * blockSize; }
// ATI2 3Dc internal static void CompressBC5Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, ImageFormats.ImageEngineFormatDetails formatDetails) { // Green: Channel 1. Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition, 1, false, formatDetails); // Red: Channel 0, 8 destination offset to be after Green. Compress8BitBlock(imgData, sourcePosition, sourceLineLength, destination, destPosition + 8, 2, false, formatDetails); }
internal static byte[] SaveWithCodecs(byte[] imageData, ImageEngineFormat format, int width, int height, AlphaSettings alphaSetting) { var image = UsefulThings.WPF.Images.CreateWriteableBitmap(imageData, width, height); image.Freeze(); BitmapFrame frame = null; if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { frame = BitmapFrame.Create(new FormatConvertedBitmap(image, PixelFormats.Bgr32, image.Palette, 0)); } else if (format == ImageEngineFormat.BMP || format == ImageEngineFormat.PNG) { // Check if there's any alpha. bool anyAlpha = false; for (int i = 3; i < imageData.Length; i += 4) { if (imageData[i] != 255) { anyAlpha = true; break; } } if (!anyAlpha) { frame = BitmapFrame.Create(new FormatConvertedBitmap(image, PixelFormats.Bgr32, image.Palette, 0)); } } else { frame = BitmapFrame.Create(image); } frame.Freeze(); // KFreon: Choose encoder based on desired format. BitmapEncoder encoder = null; int estimatedImageSize = 0; int estimateHeaderSize = 1024; switch (format) { case ImageEngineFormat.BMP: encoder = new BmpBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height * 4; // Fairly good estimation break; case ImageEngineFormat.JPG: encoder = new JpegBitmapEncoder(); ((JpegBitmapEncoder)encoder).QualityLevel = JPGCompressionSetting; estimatedImageSize = estimateHeaderSize + width * height / 6; // Estimation break; case ImageEngineFormat.PNG: encoder = new PngBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height / 2; // Estimation break; case ImageEngineFormat.GIF: encoder = new GifBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height / 5; // Estimation break; case ImageEngineFormat.TIF: encoder = new TiffBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height; // Esimation break; default: throw new InvalidOperationException($"Unable to encode format: {format} using Windows 8.1 Codecs."); } encoder.Frames.Add(frame); using (MemoryStream ms = new MemoryStream(estimatedImageSize)) // Big enough to reduce memory copying. { encoder.Save(ms); return(ms.ToArray()); } }
/// <summary> /// Save mipmaps as given format to stream. /// </summary> /// <param name="MipMaps">List of Mips to save.</param> /// <param name="mipChoice">Determines how to handle mipmaps.</param> /// <param name="maxDimension">Maximum value for either image dimension.</param> /// <param name="alphaSetting">Determines how to handle alpha.</param> /// <param name="mipToSave">0 based index on which mipmap to make top of saved image.</param> /// <param name="destFormatDetails">Details about the destination format.</param> /// <returns>True on success.</returns> internal static byte[] Save(List <MipMap> MipMaps, ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling mipChoice, AlphaSettings alphaSetting, int maxDimension = 0, int mipToSave = 0) { List <MipMap> newMips = new List <MipMap>(MipMaps); int width = newMips[0].Width; int height = newMips[0].Height; if ((destFormatDetails.IsMippable && mipChoice == MipHandling.GenerateNew) || (destFormatDetails.IsMippable && newMips.Count == 1 && mipChoice == MipHandling.Default)) { DDSGeneral.BuildMipMaps(newMips); } // KFreon: Resize if asked if (maxDimension != 0 && maxDimension < width && maxDimension < height) { if (!UsefulThings.General.IsPowerOfTwo(maxDimension)) { throw new ArgumentException($"{nameof(maxDimension)} must be a power of 2. Got {nameof(maxDimension)} = {maxDimension}"); } // KFreon: Check if there's a mipmap suitable, removes all larger mipmaps var validMipmap = newMips.Where(img => (img.Width == maxDimension && img.Height <= maxDimension) || (img.Height == maxDimension && img.Width <= maxDimension)); // Check if a mip dimension is maxDimension and that the other dimension is equal or smaller if (validMipmap?.Count() != 0) { int index = newMips.IndexOf(validMipmap.First()); newMips.RemoveRange(0, index); } else { // KFreon: Get the amount the image needs to be scaled. Find largest dimension and get it's scale. double scale = maxDimension * 1d / (width > height ? width : height); // KFreon: No mip. Resize. newMips[0] = Resize(newMips[0], scale); } } // KFreon: Ensure we have a power of two for dimensions FOR DDS ONLY TestDDSMipSize(newMips, destFormatDetails, width, height, out double fixXScale, out double fixYScale, mipChoice); if (fixXScale != 0 || fixYScale != 0 || mipChoice == MipHandling.KeepTopOnly) { DestroyMipMaps(newMips, mipToSave); } if ((fixXScale != 0 || fixXScale != 0) && destFormatDetails.IsMippable && mipChoice != MipHandling.KeepTopOnly) { DDSGeneral.BuildMipMaps(newMips); } byte[] destination = null; if (destFormatDetails.IsDDS) { destination = DDSGeneral.Save(newMips, destFormatDetails, alphaSetting); } else { // KFreon: Try saving with built in codecs var mip = newMips[0]; // Fix formatting byte[] newPixels = new byte[mip.Width * mip.Height * 4]; for (int i = 0, j = 0; i < newPixels.Length; i++, j += mip.LoadedFormatDetails.ComponentSize) { newPixels[i] = mip.LoadedFormatDetails.ReadByte(mip.Pixels, j); } destination = WIC_Codecs.SaveWithCodecs(newPixels, destFormatDetails.Format, mip.Width, mip.Height, alphaSetting); } return(destination); }
byte[] AttemptSaveUsingOriginalData(ImageFormats.ImageEngineFormatDetails destFormatDetails, MipHandling GenerateMips, int desiredMaxDimension, int mipToSave, AlphaSettings alphaSetting) { int start = 0; int destStart = 0; int length = OriginalData.Length; int newWidth = Width; int newHeight = Height; DDS_Header tempHeader = null; byte[] data = null; byte[] tempOriginalData = OriginalData; if (destFormatDetails.IsDDS) { destStart = destFormatDetails.HeaderSize; start = destStart; int mipCount = 0; if (mipToSave != 0) { mipCount = 1; newWidth = MipMaps[mipToSave].Width; newHeight = MipMaps[mipToSave].Height; start = ImageFormats.GetCompressedSize(mipToSave, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); } else if (desiredMaxDimension != 0 && desiredMaxDimension < Width && desiredMaxDimension < Height) { int index = MipMaps.FindIndex(t => t.Width < desiredMaxDimension && t.Height < desiredMaxDimension); // If none found, do a proper save and see what happens. if (index == -1) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount -= index; newWidth = MipMaps[index].Width; newHeight = MipMaps[index].Height; start = ImageFormats.GetCompressedSize(index, destFormatDetails, Width, Height); length = ImageFormats.GetCompressedSize(mipCount, destFormatDetails, newWidth, newHeight); } else { if (alphaSetting == AlphaSettings.RemoveAlphaChannel) { // Can't edit alpha directly in premultiplied formats. Not easily anyway. if (destFormatDetails.IsPremultipliedFormat) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } // DDS Formats only switch (destFormatDetails.Format) { // Excluded cos they have no true alpha case ImageEngineFormat.DDS_A8: case ImageEngineFormat.DDS_A8L8: case ImageEngineFormat.DDS_ATI1: case ImageEngineFormat.DDS_ATI2_3Dc: case ImageEngineFormat.DDS_V8U8: case ImageEngineFormat.DDS_G16_R16: case ImageEngineFormat.DDS_G8_L8: case ImageEngineFormat.DDS_R5G6B5: case ImageEngineFormat.DDS_RGB_8: case ImageEngineFormat.DDS_DXT1: break; // Exluded cos they're alpha isn't easily edited case ImageEngineFormat.DDS_DXT2: case ImageEngineFormat.DDS_DXT4: break; // Excluded cos they're currently unsupported case ImageEngineFormat.DDS_CUSTOM: case ImageEngineFormat.DDS_DX10: case ImageEngineFormat.DDS_ARGB_4: break; case ImageEngineFormat.DDS_ABGR_8: case ImageEngineFormat.DDS_ARGB_32F: case ImageEngineFormat.DDS_ARGB_8: case ImageEngineFormat.DDS_DXT3: case ImageEngineFormat.DDS_DXT5: tempOriginalData = new byte[OriginalData.Length]; Array.Copy(OriginalData, tempOriginalData, OriginalData.Length); // Edit alpha values int alphaStart = 128; int alphaJump = 0; byte[] alphaBlock = null; if (destFormatDetails.IsBlockCompressed) { alphaJump = 16; alphaBlock = new byte[8]; for (int i = 0; i < 8; i++) { alphaBlock[i] = 255; } } else { alphaJump = destFormatDetails.ComponentSize * 4; alphaBlock = new byte[destFormatDetails.ComponentSize]; switch (destFormatDetails.ComponentSize) { case 1: alphaBlock[0] = 255; break; case 2: alphaBlock = BitConverter.GetBytes(ushort.MaxValue); break; case 4: alphaBlock = BitConverter.GetBytes(1f); break; } } for (int i = alphaStart; i < OriginalData.Length; i += alphaJump) { Array.Copy(alphaBlock, 0, tempOriginalData, i, alphaBlock.Length); } break; } } switch (GenerateMips) { case MipHandling.KeepExisting: mipCount = NumMipMaps; break; case MipHandling.Default: if (NumMipMaps > 1) { mipCount = NumMipMaps; } else { goto case MipHandling.GenerateNew; // Eww goto... } break; case MipHandling.GenerateNew: ImageEngine.DestroyMipMaps(MipMaps); ImageEngine.TestDDSMipSize(MipMaps, destFormatDetails, Width, Height, out double fixXScale, out double fixYScale, GenerateMips); // Wrong sizing, so can't use original data anyway. if (fixXScale != 0 || fixYScale != 0) { return(ImageEngine.Save(MipMaps, destFormatDetails, GenerateMips, alphaSetting, desiredMaxDimension, mipToSave)); } mipCount = DDSGeneral.BuildMipMaps(MipMaps); // Compress mipmaps excl top byte[] formattedMips = DDSGeneral.Save(MipMaps.GetRange(1, MipMaps.Count - 1), destFormatDetails, alphaSetting); if (formattedMips == null) { return(null); } // Get top mip size and create destination array length = ImageFormats.GetCompressedSize(0, destFormatDetails, newWidth, newHeight); // Should be the length of the top mipmap. data = new byte[formattedMips.Length + length]; // Copy smaller mips to destination Array.Copy(formattedMips, destFormatDetails.HeaderSize, data, length, formattedMips.Length - destFormatDetails.HeaderSize); break; case MipHandling.KeepTopOnly: mipCount = 1; length = ImageFormats.GetCompressedSize(1, destFormatDetails, newWidth, newHeight); break; } } // Header tempHeader = new DDS_Header(mipCount, newHeight, newWidth, destFormatDetails.Format, destFormatDetails.DX10Format); } // Use existing array, otherwise create one. data = data ?? new byte[length]; Array.Copy(tempOriginalData, start, data, destStart, length - destStart); // Write header if existing (DDS Only) if (tempHeader != null) { tempHeader.WriteToArray(data, 0); } return(data); }
static int CheckDXT1TexelFullTransparency(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting, double alphaRef) { int uColourKey = 0; int position = sourcePosition; // Alpha stuff for (int i = 1; i <= 4; i++) { for (int j = 0; j < 4; j++) { RGBColour colour = ReadColourFromTexel(imgData, position, alphaSetting == AlphaSettings.Premultiply); if (colour.a < alphaRef) uColourKey++; position += 4; } position = sourcePosition + sourceLineLength * i; } if (uColourKey == 16) { // Entire texel is transparent for (int i = 0; i < 8; i++) destination[destPosition + i] = byte.MaxValue; return -1; } return uColourKey > 0 ? 3 : 4; }
internal static byte[] SaveWithCodecs(byte[] imageData, ImageEngineFormat format, int width, int height, AlphaSettings alphaSetting) { var image = UsefulThings.WPF.Images.CreateWriteableBitmap(imageData, width, height); image.Freeze(); BitmapFrame frame = null; if (alphaSetting == AlphaSettings.RemoveAlphaChannel) frame = BitmapFrame.Create(new FormatConvertedBitmap(image, PixelFormats.Bgr32, image.Palette, 0)); else frame = BitmapFrame.Create(image); frame.Freeze(); // KFreon: Choose encoder based on desired format. BitmapEncoder encoder = null; int estimatedImageSize = 0; int estimateHeaderSize = 1024; switch (format) { case ImageEngineFormat.BMP: encoder = new BmpBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height * 4; // Fairly good estimation break; case ImageEngineFormat.JPG: encoder = new JpegBitmapEncoder(); ((JpegBitmapEncoder)encoder).QualityLevel = JPGCompressionSetting; estimatedImageSize = estimateHeaderSize + width * height / 6; // Estimation break; case ImageEngineFormat.PNG: encoder = new PngBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height / 2; // Estimation break; case ImageEngineFormat.GIF: encoder = new GifBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height / 5; // Estimation break; case ImageEngineFormat.TIF: encoder = new TiffBitmapEncoder(); estimatedImageSize = estimateHeaderSize + width * height; // Esimation break; default: throw new InvalidOperationException($"Unable to encode format: {format} using Windows 8.1 Codecs."); } encoder.Frames.Add(frame); using (MemoryStream ms = new MemoryStream(estimatedImageSize)) // Big enough to reduce memory copying. { encoder.Save(ms); return ms.ToArray(); } }
static uint DoOtherColourFixErrorCorrection(byte[] imgData, int sourcePosition, int sourceLineLength, int uSteps, double alphaRef, AlphaSettings alphaSetting, RGBColour[] step, RGBColour Dir) { uint dw = 0; RGBColour[] Error = new RGBColour[16]; uint[] psteps = uSteps == 3 ? psteps3 : psteps4; for (int i = 0; i < 4; i++) { int position = sourcePosition + sourceLineLength * i; for (int j = 0; j < 4; j++) { int index = (i << 2) + j; RGBColour current = ReadColourFromTexel(imgData, position, alphaSetting == AlphaSettings.Premultiply); if ((uSteps == 3) && (current.a < alphaRef)) { dw = (uint)((3 << 30) | (dw >> 2)); continue; } current.r *= Luminance.r; current.g *= Luminance.g; current.b *= Luminance.b; if (true) // dither { // Error again current.r += Error[index].r; current.g += Error[index].g; current.b += Error[index].b; } float fdot = (current.r - step[0].r) * Dir.r + (current.g - step[0].g) * Dir.g + (current.b - step[0].b) * Dir.b; uint iStep = 0; if (fdot <= 0f) iStep = 0; else if (fdot >= (uSteps - 1)) iStep = 1; else iStep = psteps[(int)(fdot + .5f)]; dw = (iStep << 30) | (dw >> 2); // THIS IS THE MAGIC here. This is the "list" of indicies. Somehow... DoSomeDithering(current, index, step, (int)iStep, Error); position += 4; } } return dw; }
static byte SignedAdjustment = 128; // KFreon: This is for adjusting out of signed land. This gets removed on load and re-added on save. #region Compressed internal static void CompressBC1Block(byte[] imgData, int sourcePosition, int sourceLineLength, byte[] destination, int destPosition, AlphaSettings alphaSetting) { CompressRGBTexel(imgData, sourcePosition, sourceLineLength, destination, destPosition, true, (alphaSetting == AlphaSettings.RemoveAlphaChannel ? 0 : DXT1AlphaThreshold), alphaSetting); }