Skip to content
This repository was archived by the owner on Nov 27, 2018. It is now read-only.

Commit f833599

Browse files
committed
Added ability to separate channels and merge them back together. Seems to work.
1 parent 70e51cb commit f833599

8 files changed

Lines changed: 561 additions & 12 deletions

File tree

CSharpImageLibrary/ImageEngine.cs

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,48 @@ internal static AbstractHeader LoadHeader(Stream stream)
196196
return header;
197197
}
198198

199+
internal static void SplitChannels(MipMap mip, string savePath)
200+
{
201+
char[] channels = new char[] { 'B', 'G', 'R', 'A' };
202+
for (int i = 0; i < 4; i++)
203+
{
204+
// Extract channel into grayscale image
205+
var grayChannel = BuildGrayscaleFromChannel(mip.Pixels, i);
206+
207+
// Save channel
208+
var gray = UsefulThings.WPF.Images.CreateWriteableBitmap(grayChannel, mip.Width, mip.Height);
209+
PngBitmapEncoder encoder = new PngBitmapEncoder();
210+
encoder.Frames.Add(BitmapFrame.Create(gray));
211+
byte[] bytes = null;
212+
using (MemoryStream ms = new MemoryStream(grayChannel.Length))
213+
{
214+
encoder.Save(ms);
215+
bytes = ms.ToArray();
216+
}
217+
218+
if (bytes == null)
219+
throw new InvalidDataException("Failed to save channel. Reason unknown.");
220+
221+
string tempPath = Path.GetFileNameWithoutExtension(savePath) + "_" + channels[i] + ".png";
222+
string channelPath = Path.Combine(Path.GetDirectoryName(savePath), UsefulThings.General.FindValidNewFileName(tempPath));
223+
File.WriteAllBytes(channelPath, bytes);
224+
}
225+
}
226+
227+
static byte[] BuildGrayscaleFromChannel(byte[] pixels, int channel)
228+
{
229+
byte[] destination = new byte[pixels.Length];
230+
int count = 0;
231+
for (int i = channel; i < pixels.Length; i+=4)
232+
{
233+
for (int j = 0; j < 3; j++)
234+
destination[count++] = pixels[i];
235+
destination[count++] = 0xFF;
236+
}
237+
238+
return destination;
239+
}
240+
199241

200242
/// <summary>
201243
/// Save mipmaps as given format to stream.
@@ -295,7 +337,7 @@ internal static byte[] Save(List<MipMap> MipMaps, ImageEngineFormat format, MipH
295337
internal static MipMap Resize(MipMap mipMap, double scale, bool preserveAspect = true)
296338
{
297339
if (preserveAspect)
298-
return Resize(mipMap, scale, 0); // Could be either scale dimension, doesn't matter.
340+
return Resize(mipMap, scale, scale); // Could be either scale dimension, doesn't matter.
299341
else
300342
return Resize(mipMap, scale, scale);
301343
}

CSharpImageLibrary/ImageEngineImage.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ public ImageEngineImage(byte[] imageData, int maxDimension = 0)
143143
/// <param name="mip">Mipmap to use as source.</param>
144144
public ImageEngineImage(MipMap mip)
145145
{
146+
if (MipMaps == null)
147+
MipMaps = new List<MipMap>();
146148
MipMaps.Add(mip);
147149
}
148150

@@ -190,6 +192,16 @@ public async Task Save(string destination, ImageEngineFormat format, MipHandling
190192
await fs.WriteAsync(data, 0, data.Length);
191193
}
192194

195+
196+
/// <summary>
197+
/// Saves each channel separately incl Alpha.
198+
/// </summary>
199+
/// <param name="savePath">General save path. Appends channel name too.</param>
200+
public void SplitChannels(string savePath)
201+
{
202+
ImageEngine.SplitChannels(MipMaps[0], savePath);
203+
}
204+
193205
/// <summary>
194206
/// Saves image in specified format to stream.
195207
/// Stream position not reset before or after.
@@ -238,8 +250,7 @@ public byte[] Save(ImageEngineFormat format, MipHandling GenerateMips, int desir
238250
/// </summary>
239251
public void Dispose()
240252
{
241-
if (MipMaps == null)
242-
return;
253+
// Nothing for now I guess...
243254
}
244255

245256
/// <summary>

UI_Project/MergeChannelsImage.cs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
using CSharpImageLibrary;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Linq;
6+
using System.Text;
7+
using System.Threading.Tasks;
8+
using System.Windows.Media.Imaging;
9+
using UsefulThings.WPF;
10+
11+
namespace UI_Project
12+
{
13+
public class MergeChannelsImage : ViewModelBase
14+
{
15+
#region Properties
16+
public byte[] Pixels { get; private set; }
17+
public string FilePath { get; private set; }
18+
19+
public string DisplayName
20+
{
21+
get
22+
{
23+
return Path.GetFileNameWithoutExtension(FilePath);
24+
}
25+
}
26+
27+
bool isRed = false;
28+
public bool IsRed
29+
{
30+
get
31+
{
32+
return isRed;
33+
}
34+
set
35+
{
36+
SetProperty(ref isRed, value);
37+
}
38+
}
39+
40+
bool isGreen = false;
41+
public bool IsGreen
42+
{
43+
get
44+
{
45+
return isGreen;
46+
}
47+
set
48+
{
49+
SetProperty(ref isGreen, value);
50+
}
51+
}
52+
53+
bool isBlue = false;
54+
public bool IsBlue
55+
{
56+
get
57+
{
58+
return isBlue;
59+
}
60+
set
61+
{
62+
SetProperty(ref isBlue, value);
63+
}
64+
}
65+
66+
bool isAlpha = false;
67+
public bool IsAlpha
68+
{
69+
get
70+
{
71+
return isAlpha;
72+
}
73+
set
74+
{
75+
SetProperty(ref isAlpha, value);
76+
}
77+
}
78+
79+
public BitmapSource Thumbnail { get; private set; }
80+
81+
public int Height { get; private set; }
82+
public int Width { get; private set; }
83+
#endregion Properties
84+
85+
86+
public MergeChannelsImage(string mainPath)
87+
{
88+
FilePath = mainPath;
89+
90+
using (var img = new ImageEngineImage(mainPath))
91+
{
92+
Thumbnail = img.GetWPFBitmap(128);
93+
Width = img.Width;
94+
Height = img.Height;
95+
Pixels = img.MipMaps[0].Pixels;
96+
}
97+
}
98+
}
99+
}

UI_Project/NewMainWindow.xaml

Lines changed: 113 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,10 @@
487487
Visibility="{Binding SettingsPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>
488488
<Button x:Name="InfoPanelOpenButton" Content="System Information" DockPanel.Dock="Right" Margin="0,1,10,1" Click="InfoPanelOpenButton_Click"
489489
Visibility="{Binding InfoPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>
490+
491+
<Button x:Name="ChannelMergerOpenButton" Content="Merge Channels" DockPanel.Dock="Right" Margin="0,1,10,1" Click="ChannelMergerOpenButton_Click"
492+
Visibility="{Binding MergeChannelsPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>
493+
490494
<TextBlock x:Name="TitleBlock" Text="{Binding WindowTitle}" Foreground="White" VerticalAlignment="Center" Margin="10,0,0,0" IsHitTestVisible="False" FontSize="22"/>
491495
</DockPanel>
492496

@@ -804,8 +808,11 @@
804808
<TextBox x:Name="SavePathBox" Text="{Binding SavePath, UpdateSourceTrigger=PropertyChanged}" Margin="5,0" LostFocus="SavePathBox_LostFocus"/>
805809
</DockPanel>
806810

811+
<CheckBox x:Name="ChannelSeparatorCheckbox" DockPanel.Dock="Top" Content="Save Channels Separately" Foreground="White" Margin="3,10,0,0"
812+
IsChecked="{Binding SplitChannels}"/>
807813

808-
<DockPanel x:Name="SaveFormatSelectionArea" DockPanel.Dock="Top" LastChildFill="False">
814+
<DockPanel x:Name="SaveFormatSelectionArea" DockPanel.Dock="Top" LastChildFill="False"
815+
Visibility="{Binding SplitChannels, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}">
809816
<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom">
810817
<TextBlock Style="{StaticResource VisibleTextDisplayStyle}" TextAlignment="Center" FontSize="16">
811818
<Run Text="Compressed Size: " FontWeight="Bold"/>
@@ -840,16 +847,31 @@
840847
<Label Content="Format Details" Style="{StaticResource TitleStyle}" FontSize="18" DockPanel.Dock="Bottom"/>
841848
<ContentControl x:Name="CustomDDSSettingsPanel" DockPanel.Dock="Top" Margin="0,5" Template="{StaticResource CustomDDSSettings}" Style="{StaticResource CustomDDSSettingsVisibility}"/>
842849
<ContentControl x:Name="MainSaveFormatSelector" DockPanel.Dock="Top" Margin="0,5" Template="{StaticResource MainFormatSelector}"/>
843-
844850
</DockPanel>
845851

846-
<DockPanel x:Name="FormatSpecificSettingsArea" DockPanel.Dock="Top">
852+
<DockPanel x:Name="FormatSpecificSettingsArea" DockPanel.Dock="Top" Visibility="{Binding SplitChannels, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}">
847853
<ContentControl x:Name="GeneralAlphaSettings" DockPanel.Dock="Top" Template="{StaticResource GeneralAlphaSettingsTemplate}" Style="{StaticResource GeneralAlphaSettingsPanelVisibility}"/>
848854
<ContentControl x:Name="DXT1AlphaSettings" DockPanel.Dock="Top" Style="{StaticResource DXT1AlphaSettingVisibility}" Template="{StaticResource DXT1AlphaSettingTemplate}"/>
849855
<ContentControl x:Name="JPGQualitySettings" DockPanel.Dock="Top" Style="{StaticResource JPGSettingsVisibility}" Template="{StaticResource JPGSettingsTemplate}"/>
850856
</DockPanel>
851857

852-
<ContentControl x:Name="MipSettingsArea" DockPanel.Dock="Top" Visibility="{Binding IsSaveFormatMippable, Converter={StaticResource BoolToVisConverter}}" Margin="0,10,0,0" Template="{StaticResource MipSettingsTemplate}"/>
858+
<ContentControl x:Name="MipSettingsArea" DockPanel.Dock="Top" Margin="0,10,0,0" Template="{StaticResource MipSettingsTemplate}">
859+
<ContentControl.Style>
860+
<Style TargetType="FrameworkElement">
861+
<Setter Property="Visibility" Value="Collapsed"/>
862+
863+
<Style.Triggers>
864+
<DataTrigger Binding="{Binding IsSaveFormatMippable}" Value="True">
865+
<Setter Property="Visibility" Value="Visible"/>
866+
</DataTrigger>
867+
868+
<DataTrigger Binding="{Binding SplitChannels}" Value="True">
869+
<Setter Property="Visibility" Value="Collapsed"/>
870+
</DataTrigger>
871+
</Style.Triggers>
872+
</Style>
873+
</ContentControl.Style>
874+
</ContentControl>
853875
</DockPanel>
854876
</ScrollViewer>
855877
</DockPanel>
@@ -1051,5 +1073,92 @@
10511073
</DockPanel>
10521074
</Border>
10531075
</Border>
1076+
1077+
<Border x:Name="MergeChannelPanel" Style="{StaticResource FadedBackgroundBorder}" Tag="{Binding MergeChannelsPanelOpen}" Drop="MergeChannelPanel_Drop" DragOver="MergeChannelPanel_DragOver">
1078+
<Border Style="{StaticResource FadedInnerBorder}">
1079+
<DockPanel Margin="5,0,5,5">
1080+
<Label Content="Merge Channels" Style="{StaticResource TitleStyle}" DockPanel.Dock="Top"/>
1081+
<Image MaxWidth="1024" MaxHeight="1024" DockPanel.Dock="Bottom"/>
1082+
<DockPanel DockPanel.Dock="Bottom" Margin="5,10,5,10">
1083+
<Button x:Name="MergeLoadButton" Content="Load" DockPanel.Dock="Left" Click="MergeLoadButton_Click" />
1084+
<Button x:Name="MergeCloseButton" Content="Close" DockPanel.Dock="Right" Click="MergeCloseButton_Click"/>
1085+
<Button x:Name="MergeMergeButton" Content="Merge!" Click="MergeMergeButton_Click" FontWeight="Bold" Margin="10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
1086+
<Button.Style>
1087+
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="Button">
1088+
<Setter Property="Visibility" Value="Visible"/>
1089+
1090+
<Style.Triggers>
1091+
<DataTrigger Binding="{Binding MergeChannelsImages.Count}" Value="0">
1092+
<Setter Property="Visibility" Value="Collapsed"/>
1093+
</DataTrigger>
1094+
1095+
<DataTrigger Binding="{Binding MergeChannelsReady}" Value="False">
1096+
<Setter Property="Visibility" Value="Collapsed"/>
1097+
</DataTrigger>
1098+
</Style.Triggers>
1099+
</Style>
1100+
</Button.Style>
1101+
</Button>
1102+
</DockPanel>
1103+
<ListBox x:Name="MergeChannelSourceBox" DockPanel.Dock="Top" ItemsSource="{Binding MergeChannelsImages}" Background="Transparent">
1104+
<ListBox.ItemTemplate>
1105+
<ItemContainerTemplate>
1106+
<DockPanel MaxWidth="300" MaxHeight="300">
1107+
<DockPanel.Style>
1108+
<Style TargetType="DockPanel">
1109+
<Setter Property="Background" Value="Transparent"/>
1110+
1111+
<Style.Triggers>
1112+
<DataTrigger Binding="{Binding IsAlpha}" Value="true">
1113+
<Setter Property="Background" Value="#7F808080"/>
1114+
</DataTrigger>
1115+
1116+
<DataTrigger Binding="{Binding IsRed}" Value="true">
1117+
<Setter Property="Background" Value="#7FFF0000"/>
1118+
</DataTrigger>
1119+
1120+
<DataTrigger Binding="{Binding IsGreen}" Value="true">
1121+
<Setter Property="Background" Value="#7F008000"/>
1122+
</DataTrigger>
1123+
1124+
<DataTrigger Binding="{Binding IsBlue}" Value="true">
1125+
<Setter Property="Background" Value="#7F0000FF"/>
1126+
</DataTrigger>
1127+
</Style.Triggers>
1128+
</Style>
1129+
</DockPanel.Style>
1130+
<Image MaxWidth="128" MaxHeight="128" DockPanel.Dock="Top" Source="{Binding Thumbnail}"/>
1131+
<DockPanel Background="Transparent" LastChildFill="False">
1132+
<DockPanel.Resources>
1133+
<Style TargetType="Button">
1134+
<Setter Property="Background" Value="Transparent"/>
1135+
<Setter Property="Width" Value="20"/>
1136+
<Setter Property="Height" Value="20"/>
1137+
<Setter Property="Margin" Value="2"/>
1138+
<Setter Property="Foreground" Value="White"/>
1139+
</Style>
1140+
</DockPanel.Resources>
1141+
1142+
<TextBlock Text="{Binding DisplayName}" DockPanel.Dock="Top" Foreground="White" TextWrapping="Wrap"/>
1143+
1144+
<Label Content="Set as Channel:" Foreground="White" DockPanel.Dock="Left"/>
1145+
<Button x:Name="MergeRedSelector" Content="R" Click="MergeRedSelector_Click" DockPanel.Dock="Left"/>
1146+
<Button x:Name="MergeGreenSelector" Content="G" Click="MergeGreenSelector_Click" DockPanel.Dock="Left"/>
1147+
<Button x:Name="MergeBlueSelector" Content="B" Click="MergeBlueSelector_Click" DockPanel.Dock="Left"/>
1148+
<Button x:Name="MergeAlphaSelector" Content="A" Click="MergeAlphaSelector_Click" DockPanel.Dock="Left"/>
1149+
<Button x:Name="MergeDeselector" Content="X" Foreground="Red" BorderBrush="Red" BorderThickness="0.5" DockPanel.Dock="Left" Click="MergeDeselector_Click"/>
1150+
</DockPanel>
1151+
</DockPanel>
1152+
</ItemContainerTemplate>
1153+
</ListBox.ItemTemplate>
1154+
<ListBox.ItemsPanel>
1155+
<ItemsPanelTemplate>
1156+
<WrapPanel MaxWidth="{Binding ElementName=MergeChannelPanel, Path=ActualWidth}"/>
1157+
</ItemsPanelTemplate>
1158+
</ListBox.ItemsPanel>
1159+
</ListBox>
1160+
</DockPanel>
1161+
</Border>
1162+
</Border>
10541163
</Grid>
10551164
</Window>

0 commit comments

Comments
 (0)