From ea09a8de6407206875d34797f104fe755c388a4f Mon Sep 17 00:00:00 2001 From: VaDiM Date: Mon, 21 Nov 2022 01:52:40 +0300 Subject: [PATCH] Add support for sprites with alpha mask - Sprites with alpha mask can now be viewed and exported with transparency - Added hotkeys to control display of an alpha mask on the preview tab - Added an option to the export settings to enable/disable export with alpha mask as well - Prevented texture2D preview options from being changed with hotkeys outside of texture preview (e.g. when some other asset is selected) --- AssetStudioGUI/AssetStudioGUI.csproj | 144 +++++++++--------- AssetStudioGUI/AssetStudioGUIForm.cs | 63 +++++--- AssetStudioGUI/ExportOptions.Designer.cs | 19 ++- AssetStudioGUI/ExportOptions.cs | 3 +- AssetStudioGUI/ExportOptions.resx | 3 - AssetStudioGUI/Exporter.cs | 3 +- .../Properties/Settings.Designer.cs | 22 ++- AssetStudioGUI/Properties/Settings.settings | 3 + AssetStudioUtility/SpriteHelper.cs | 52 ++++++- 9 files changed, 208 insertions(+), 104 deletions(-) diff --git a/AssetStudioGUI/AssetStudioGUI.csproj b/AssetStudioGUI/AssetStudioGUI.csproj index 5cc3ac9..95b02bb 100644 --- a/AssetStudioGUI/AssetStudioGUI.csproj +++ b/AssetStudioGUI/AssetStudioGUI.csproj @@ -1,83 +1,83 @@  - - WinExe - net472;net6.0-windows - true - Resources\as.ico - AssetStudio Mod by VaDiM - 0.16.47.1 - Copyright © Perfare 2018-2022 - embedded - + + WinExe + net472;net6.0-windows + true + Resources\as.ico + AssetStudio Mod by VaDiM + 0.16.47.1 + Copyright © Perfare 2018-2022 + embedded + - - - - + + + + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + True + Settings.settings + + - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - + + + ResXFileCodeGenerator + Resources.Designer.cs + + + True + Resources.resx + True + + - - - ResXFileCodeGenerator - Resources.Designer.cs - - - True - Resources.resx - True - - + + + PreserveNewest + x86\fmod.dll + + + PreserveNewest + x64\fmod.dll + + - - - PreserveNewest - x86\fmod.dll - - - PreserveNewest - x64\fmod.dll - - + + + + Libraries\OpenTK.WinForms.dll + + - - - - Libraries\OpenTK.WinForms.dll - - + + + + - - - - + + + - - - + + + + + + - - - - - - - - - - - - - + + + + + + \ No newline at end of file diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 8389bf2..0aaa307 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -43,6 +43,10 @@ namespace AssetStudioGUI private uint FMODlenms; private float FMODVolume = 0.8f; + #region SpriteControl + private SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On; + #endregion + #region TexControl private static char[] textureChannelNames = new[] { 'B', 'G', 'R', 'A' }; private bool[] textureChannels = new[] { true, true, true, true }; @@ -315,24 +319,41 @@ namespace AssetStudioGUI if (e.Control) { var need = false; - switch (e.KeyCode) + if (lastSelectedItem.Type == ClassIDType.Texture2D) { - case Keys.B: - textureChannels[0] = !textureChannels[0]; - need = true; - break; - case Keys.G: - textureChannels[1] = !textureChannels[1]; - need = true; - break; - case Keys.R: - textureChannels[2] = !textureChannels[2]; - need = true; - break; - case Keys.A: - textureChannels[3] = !textureChannels[3]; - need = true; - break; + switch (e.KeyCode) + { + case Keys.B: + textureChannels[0] = !textureChannels[0]; + need = true; + break; + case Keys.G: + textureChannels[1] = !textureChannels[1]; + need = true; + break; + case Keys.R: + textureChannels[2] = !textureChannels[2]; + need = true; + break; + case Keys.A: + textureChannels[3] = !textureChannels[3]; + need = true; + break; + } + } + else if (lastSelectedItem.Type == ClassIDType.Sprite && !((Sprite)lastSelectedItem.Asset).m_RD.alphaTexture.IsNull) + { + switch (e.KeyCode) + { + case Keys.A: + spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.On ? SpriteMaskMode.Off : SpriteMaskMode.On; + need = true; + break; + case Keys.M: + spriteMaskVisibleMode = spriteMaskVisibleMode == SpriteMaskMode.MaskOnly ? SpriteMaskMode.On : SpriteMaskMode.MaskOnly; + need = true; + break; + } } if (need) { @@ -1170,13 +1191,19 @@ namespace AssetStudioGUI private void PreviewSprite(AssetItem assetItem, Sprite m_Sprite) { - var image = m_Sprite.GetImage(); + var image = m_Sprite.GetImage(spriteMaskVisibleMode); if (image != null) { var bitmap = new DirectBitmap(image.ConvertToBytes(), image.Width, image.Height); image.Dispose(); assetItem.InfoText = $"Width: {bitmap.Width}\nHeight: {bitmap.Height}\n"; PreviewTexture(bitmap); + + if (!m_Sprite.m_RD.alphaTexture.IsNull) + { + assetItem.InfoText += $"Alpha Mask: {spriteMaskVisibleMode}\n"; + StatusStripUpdate("'Ctrl'+'A' - Enable/Disable alpha mask usage. 'Ctrl'+'M' - Show alpha mask only."); + } } else { diff --git a/AssetStudioGUI/ExportOptions.Designer.cs b/AssetStudioGUI/ExportOptions.Designer.cs index 1d7f694..943cf49 100644 --- a/AssetStudioGUI/ExportOptions.Designer.cs +++ b/AssetStudioGUI/ExportOptions.Designer.cs @@ -32,6 +32,7 @@ this.OKbutton = new System.Windows.Forms.Button(); this.Cancel = new System.Windows.Forms.Button(); this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.exportSpriteWithAlphaMask = new System.Windows.Forms.CheckBox(); this.openAfterExport = new System.Windows.Forms.CheckBox(); this.restoreExtensionName = new System.Windows.Forms.CheckBox(); this.assetGroupOptions = new System.Windows.Forms.ComboBox(); @@ -94,6 +95,7 @@ // groupBox1 // this.groupBox1.AutoSize = true; + this.groupBox1.Controls.Add(this.exportSpriteWithAlphaMask); this.groupBox1.Controls.Add(this.openAfterExport); this.groupBox1.Controls.Add(this.restoreExtensionName); this.groupBox1.Controls.Add(this.assetGroupOptions); @@ -108,12 +110,24 @@ this.groupBox1.TabStop = false; this.groupBox1.Text = "Export"; // + // exportSpriteWithAlphaMask + // + this.exportSpriteWithAlphaMask.AutoSize = true; + this.exportSpriteWithAlphaMask.Checked = true; + this.exportSpriteWithAlphaMask.CheckState = System.Windows.Forms.CheckState.Checked; + this.exportSpriteWithAlphaMask.Location = new System.Drawing.Point(6, 150); + this.exportSpriteWithAlphaMask.Name = "exportSpriteWithAlphaMask"; + this.exportSpriteWithAlphaMask.Size = new System.Drawing.Size(205, 17); + this.exportSpriteWithAlphaMask.TabIndex = 11; + this.exportSpriteWithAlphaMask.Text = "Export sprites with alpha mask applied"; + this.exportSpriteWithAlphaMask.UseVisualStyleBackColor = true; + // // openAfterExport // this.openAfterExport.AutoSize = true; this.openAfterExport.Checked = true; this.openAfterExport.CheckState = System.Windows.Forms.CheckState.Checked; - this.openAfterExport.Location = new System.Drawing.Point(6, 173); + this.openAfterExport.Location = new System.Drawing.Point(6, 196); this.openAfterExport.Name = "openAfterExport"; this.openAfterExport.Size = new System.Drawing.Size(137, 17); this.openAfterExport.TabIndex = 10; @@ -160,7 +174,7 @@ this.convertAudio.AutoSize = true; this.convertAudio.Checked = true; this.convertAudio.CheckState = System.Windows.Forms.CheckState.Checked; - this.convertAudio.Location = new System.Drawing.Point(6, 150); + this.convertAudio.Location = new System.Drawing.Point(6, 173); this.convertAudio.Name = "convertAudio"; this.convertAudio.Size = new System.Drawing.Size(179, 17); this.convertAudio.TabIndex = 6; @@ -531,5 +545,6 @@ private System.Windows.Forms.CheckBox openAfterExport; private System.Windows.Forms.CheckBox exportAllUvsAsDiffuseMaps; private System.Windows.Forms.ToolTip exportUvsTooltip; + private System.Windows.Forms.CheckBox exportSpriteWithAlphaMask; } } \ No newline at end of file diff --git a/AssetStudioGUI/ExportOptions.cs b/AssetStudioGUI/ExportOptions.cs index 117ceda..c3542a1 100644 --- a/AssetStudioGUI/ExportOptions.cs +++ b/AssetStudioGUI/ExportOptions.cs @@ -12,6 +12,7 @@ namespace AssetStudioGUI assetGroupOptions.SelectedIndex = Properties.Settings.Default.assetGroupOption; restoreExtensionName.Checked = Properties.Settings.Default.restoreExtensionName; converttexture.Checked = Properties.Settings.Default.convertTexture; + exportSpriteWithAlphaMask.Checked = Properties.Settings.Default.exportSpriteWithMask; convertAudio.Checked = Properties.Settings.Default.convertAudio; var str = Properties.Settings.Default.convertType.ToString(); foreach (Control c in panel1.Controls) @@ -43,6 +44,7 @@ namespace AssetStudioGUI Properties.Settings.Default.assetGroupOption = assetGroupOptions.SelectedIndex; Properties.Settings.Default.restoreExtensionName = restoreExtensionName.Checked; Properties.Settings.Default.convertTexture = converttexture.Checked; + Properties.Settings.Default.exportSpriteWithMask = exportSpriteWithAlphaMask.Checked; Properties.Settings.Default.convertAudio = convertAudio.Checked; foreach (Control c in panel1.Controls) { @@ -75,6 +77,5 @@ namespace AssetStudioGUI DialogResult = DialogResult.Cancel; Close(); } - } } diff --git a/AssetStudioGUI/ExportOptions.resx b/AssetStudioGUI/ExportOptions.resx index b60237f..6966b18 100644 --- a/AssetStudioGUI/ExportOptions.resx +++ b/AssetStudioGUI/ExportOptions.resx @@ -120,7 +120,4 @@ 17, 17 - - 17, 17 - \ No newline at end of file diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index 473ce78..41f65ae 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -231,9 +231,10 @@ namespace AssetStudioGUI public static bool ExportSprite(AssetItem item, string exportPath) { var type = Properties.Settings.Default.convertType; + var alphaMask = Properties.Settings.Default.exportSpriteWithMask ? SpriteMaskMode.On : SpriteMaskMode.Off; if (!TryExportFile(exportPath, item, "." + type.ToString().ToLower(), out var exportFullPath)) return false; - var image = ((Sprite)item.Asset).GetImage(); + var image = ((Sprite)item.Asset).GetImage(alphaMask); if (image != null) { using (image) diff --git a/AssetStudioGUI/Properties/Settings.Designer.cs b/AssetStudioGUI/Properties/Settings.Designer.cs index 2ecec40..bb52046 100644 --- a/AssetStudioGUI/Properties/Settings.Designer.cs +++ b/AssetStudioGUI/Properties/Settings.Designer.cs @@ -1,10 +1,10 @@ //------------------------------------------------------------------------------ // -// 此代码由工具生成。 -// 运行时版本:4.0.30319.42000 +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 // -// 对此文件的更改可能会导致不正确的行为,并且如果 -// 重新生成代码,这些更改将会丢失。 +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -12,7 +12,7 @@ namespace AssetStudioGUI.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.1.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); @@ -274,5 +274,17 @@ namespace AssetStudioGUI.Properties { this["exportAllUvsAsDiffuseMaps"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool exportSpriteWithMask { + get { + return ((bool)(this["exportSpriteWithMask"])); + } + set { + this["exportSpriteWithMask"] = value; + } + } } } diff --git a/AssetStudioGUI/Properties/Settings.settings b/AssetStudioGUI/Properties/Settings.settings index d6eb751..aef06e0 100644 --- a/AssetStudioGUI/Properties/Settings.settings +++ b/AssetStudioGUI/Properties/Settings.settings @@ -65,5 +65,8 @@ False + + True + \ No newline at end of file diff --git a/AssetStudioUtility/SpriteHelper.cs b/AssetStudioUtility/SpriteHelper.cs index 56f17a3..5c6a356 100644 --- a/AssetStudioUtility/SpriteHelper.cs +++ b/AssetStudioUtility/SpriteHelper.cs @@ -11,9 +11,16 @@ using System.Numerics; namespace AssetStudio { + public enum SpriteMaskMode + { + Off, + On, + MaskOnly + } + public static class SpriteHelper { - public static Image GetImage(this Sprite m_Sprite) + public static Image GetImage(this Sprite m_Sprite, SpriteMaskMode spriteMaskVisibleMode = SpriteMaskMode.On) { if (m_Sprite.m_SpriteAtlas != null && m_Sprite.m_SpriteAtlas.TryGet(out var m_SpriteAtlas)) { @@ -24,7 +31,26 @@ namespace AssetStudio } else { - if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D)) + if (m_Sprite.m_RD.texture.TryGet(out var m_Texture2D) && m_Sprite.m_RD.alphaTexture.TryGet(out var m_AlphaTexture2D) && spriteMaskVisibleMode != SpriteMaskMode.Off) + { + var tex = CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw); + var alphaTex = CutImage(m_Sprite, m_AlphaTexture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw); + + if (tex.Width != alphaTex.Width || tex.Height != alphaTex.Height) + { + alphaTex.Mutate(x => x.Resize(tex.Width, tex.Height)); + } + + switch (spriteMaskVisibleMode) + { + case SpriteMaskMode.On: + return ApplyRGBMask(tex, alphaTex); + case SpriteMaskMode.MaskOnly: + tex.Dispose(); + return alphaTex; + } + } + else if (m_Sprite.m_RD.texture.TryGet(out m_Texture2D)) { return CutImage(m_Sprite, m_Texture2D, m_Sprite.m_RD.textureRect, m_Sprite.m_RD.textureRectOffset, m_Sprite.m_RD.downscaleMultiplier, m_Sprite.m_RD.settingsRaw); } @@ -32,6 +58,28 @@ namespace AssetStudio return null; } + private static Image ApplyRGBMask(Image tex, Image texMask) + { + using (texMask) + { + tex.ProcessPixelRows(texMask, (sourceTex, targetTexMask) => + { + for (int y = 0; y < texMask.Height; y++) + { + var texRow = sourceTex.GetRowSpan(y); + var maskRow = targetTexMask.GetRowSpan(y); + for (int x = 0; x < maskRow.Length; x++) + { + var grayscale = (byte)((maskRow[x].R + maskRow[x].G + maskRow[x].B) / 3); + texRow[x].A = grayscale; + } + } + }); + + return tex; + } + } + private static Image CutImage(Sprite m_Sprite, Texture2D m_Texture2D, Rectf textureRect, Vector2 textureRectOffset, float downscaleMultiplier, SpriteSettings settingsRaw) { var originalImage = m_Texture2D.ConvertToImage(false);