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

Commit

Permalink
Added ability to separate channels and merge them back together. Seem…
Browse files Browse the repository at this point in the history
…s to work.
  • Loading branch information
KFreon committed Dec 19, 2016
1 parent 70e51cb commit f833599
Show file tree
Hide file tree
Showing 8 changed files with 561 additions and 12 deletions.
44 changes: 43 additions & 1 deletion CSharpImageLibrary/ImageEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,48 @@ internal static AbstractHeader LoadHeader(Stream stream)
return header;
}

internal static void SplitChannels(MipMap mip, string savePath)
{
char[] channels = new char[] { 'B', 'G', 'R', 'A' };
for (int i = 0; i < 4; i++)
{
// Extract channel into grayscale image
var grayChannel = BuildGrayscaleFromChannel(mip.Pixels, i);

// Save channel
var gray = UsefulThings.WPF.Images.CreateWriteableBitmap(grayChannel, mip.Width, mip.Height);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(gray));
byte[] bytes = null;
using (MemoryStream ms = new MemoryStream(grayChannel.Length))
{
encoder.Save(ms);
bytes = ms.ToArray();
}

if (bytes == null)
throw new InvalidDataException("Failed to save channel. Reason unknown.");

string tempPath = Path.GetFileNameWithoutExtension(savePath) + "_" + channels[i] + ".png";
string channelPath = Path.Combine(Path.GetDirectoryName(savePath), UsefulThings.General.FindValidNewFileName(tempPath));
File.WriteAllBytes(channelPath, bytes);
}
}

static byte[] BuildGrayscaleFromChannel(byte[] pixels, int channel)
{
byte[] destination = new byte[pixels.Length];
int count = 0;
for (int i = channel; i < pixels.Length; i+=4)
{
for (int j = 0; j < 3; j++)
destination[count++] = pixels[i];
destination[count++] = 0xFF;
}

return destination;
}


/// <summary>
/// Save mipmaps as given format to stream.
Expand Down Expand Up @@ -295,7 +337,7 @@ internal static byte[] Save(List<MipMap> MipMaps, ImageEngineFormat format, MipH
internal static MipMap Resize(MipMap mipMap, double scale, bool preserveAspect = true)
{
if (preserveAspect)
return Resize(mipMap, scale, 0); // Could be either scale dimension, doesn't matter.
return Resize(mipMap, scale, scale); // Could be either scale dimension, doesn't matter.
else
return Resize(mipMap, scale, scale);
}
Expand Down
15 changes: 13 additions & 2 deletions CSharpImageLibrary/ImageEngineImage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,8 @@ public ImageEngineImage(byte[] imageData, int maxDimension = 0)
/// <param name="mip">Mipmap to use as source.</param>
public ImageEngineImage(MipMap mip)
{
if (MipMaps == null)
MipMaps = new List<MipMap>();
MipMaps.Add(mip);
}

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


/// <summary>
/// Saves each channel separately incl Alpha.
/// </summary>
/// <param name="savePath">General save path. Appends channel name too.</param>
public void SplitChannels(string savePath)
{
ImageEngine.SplitChannels(MipMaps[0], savePath);
}

/// <summary>
/// Saves image in specified format to stream.
/// Stream position not reset before or after.
Expand Down Expand Up @@ -238,8 +250,7 @@ public byte[] Save(ImageEngineFormat format, MipHandling GenerateMips, int desir
/// </summary>
public void Dispose()
{
if (MipMaps == null)
return;
// Nothing for now I guess...
}

/// <summary>
Expand Down
99 changes: 99 additions & 0 deletions UI_Project/MergeChannelsImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
using CSharpImageLibrary;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;
using UsefulThings.WPF;

namespace UI_Project
{
public class MergeChannelsImage : ViewModelBase
{
#region Properties
public byte[] Pixels { get; private set; }
public string FilePath { get; private set; }

public string DisplayName
{
get
{
return Path.GetFileNameWithoutExtension(FilePath);
}
}

bool isRed = false;
public bool IsRed
{
get
{
return isRed;
}
set
{
SetProperty(ref isRed, value);
}
}

bool isGreen = false;
public bool IsGreen
{
get
{
return isGreen;
}
set
{
SetProperty(ref isGreen, value);
}
}

bool isBlue = false;
public bool IsBlue
{
get
{
return isBlue;
}
set
{
SetProperty(ref isBlue, value);
}
}

bool isAlpha = false;
public bool IsAlpha
{
get
{
return isAlpha;
}
set
{
SetProperty(ref isAlpha, value);
}
}

public BitmapSource Thumbnail { get; private set; }

public int Height { get; private set; }
public int Width { get; private set; }
#endregion Properties


public MergeChannelsImage(string mainPath)
{
FilePath = mainPath;

using (var img = new ImageEngineImage(mainPath))
{
Thumbnail = img.GetWPFBitmap(128);
Width = img.Width;
Height = img.Height;
Pixels = img.MipMaps[0].Pixels;
}
}
}
}
117 changes: 113 additions & 4 deletions UI_Project/NewMainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,10 @@
Visibility="{Binding SettingsPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>
<Button x:Name="InfoPanelOpenButton" Content="System Information" DockPanel.Dock="Right" Margin="0,1,10,1" Click="InfoPanelOpenButton_Click"
Visibility="{Binding InfoPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>

<Button x:Name="ChannelMergerOpenButton" Content="Merge Channels" DockPanel.Dock="Right" Margin="0,1,10,1" Click="ChannelMergerOpenButton_Click"
Visibility="{Binding MergeChannelsPanelOpen, Converter={StaticResource BoolToVisConverter}, ConverterParameter={StaticResource TrueValue}}"/>

<TextBlock x:Name="TitleBlock" Text="{Binding WindowTitle}" Foreground="White" VerticalAlignment="Center" Margin="10,0,0,0" IsHitTestVisible="False" FontSize="22"/>
</DockPanel>

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

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

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

</DockPanel>

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

<ContentControl x:Name="MipSettingsArea" DockPanel.Dock="Top" Visibility="{Binding IsSaveFormatMippable, Converter={StaticResource BoolToVisConverter}}" Margin="0,10,0,0" Template="{StaticResource MipSettingsTemplate}"/>
<ContentControl x:Name="MipSettingsArea" DockPanel.Dock="Top" Margin="0,10,0,0" Template="{StaticResource MipSettingsTemplate}">
<ContentControl.Style>
<Style TargetType="FrameworkElement">
<Setter Property="Visibility" Value="Collapsed"/>

<Style.Triggers>
<DataTrigger Binding="{Binding IsSaveFormatMippable}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>

<DataTrigger Binding="{Binding SplitChannels}" Value="True">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
</DockPanel>
</ScrollViewer>
</DockPanel>
Expand Down Expand Up @@ -1051,5 +1073,92 @@
</DockPanel>
</Border>
</Border>

<Border x:Name="MergeChannelPanel" Style="{StaticResource FadedBackgroundBorder}" Tag="{Binding MergeChannelsPanelOpen}" Drop="MergeChannelPanel_Drop" DragOver="MergeChannelPanel_DragOver">
<Border Style="{StaticResource FadedInnerBorder}">
<DockPanel Margin="5,0,5,5">
<Label Content="Merge Channels" Style="{StaticResource TitleStyle}" DockPanel.Dock="Top"/>
<Image MaxWidth="1024" MaxHeight="1024" DockPanel.Dock="Bottom"/>
<DockPanel DockPanel.Dock="Bottom" Margin="5,10,5,10">
<Button x:Name="MergeLoadButton" Content="Load" DockPanel.Dock="Left" Click="MergeLoadButton_Click" />
<Button x:Name="MergeCloseButton" Content="Close" DockPanel.Dock="Right" Click="MergeCloseButton_Click"/>
<Button x:Name="MergeMergeButton" Content="Merge!" Click="MergeMergeButton_Click" FontWeight="Bold" Margin="10,0" VerticalAlignment="Center" HorizontalAlignment="Center">
<Button.Style>
<Style BasedOn="{StaticResource {x:Type Button}}" TargetType="Button">
<Setter Property="Visibility" Value="Visible"/>

<Style.Triggers>
<DataTrigger Binding="{Binding MergeChannelsImages.Count}" Value="0">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>

<DataTrigger Binding="{Binding MergeChannelsReady}" Value="False">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</DockPanel>
<ListBox x:Name="MergeChannelSourceBox" DockPanel.Dock="Top" ItemsSource="{Binding MergeChannelsImages}" Background="Transparent">
<ListBox.ItemTemplate>
<ItemContainerTemplate>
<DockPanel MaxWidth="300" MaxHeight="300">
<DockPanel.Style>
<Style TargetType="DockPanel">
<Setter Property="Background" Value="Transparent"/>

<Style.Triggers>
<DataTrigger Binding="{Binding IsAlpha}" Value="true">
<Setter Property="Background" Value="#7F808080"/>
</DataTrigger>

<DataTrigger Binding="{Binding IsRed}" Value="true">
<Setter Property="Background" Value="#7FFF0000"/>
</DataTrigger>

<DataTrigger Binding="{Binding IsGreen}" Value="true">
<Setter Property="Background" Value="#7F008000"/>
</DataTrigger>

<DataTrigger Binding="{Binding IsBlue}" Value="true">
<Setter Property="Background" Value="#7F0000FF"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<Image MaxWidth="128" MaxHeight="128" DockPanel.Dock="Top" Source="{Binding Thumbnail}"/>
<DockPanel Background="Transparent" LastChildFill="False">
<DockPanel.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="Width" Value="20"/>
<Setter Property="Height" Value="20"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</DockPanel.Resources>

<TextBlock Text="{Binding DisplayName}" DockPanel.Dock="Top" Foreground="White" TextWrapping="Wrap"/>

<Label Content="Set as Channel:" Foreground="White" DockPanel.Dock="Left"/>
<Button x:Name="MergeRedSelector" Content="R" Click="MergeRedSelector_Click" DockPanel.Dock="Left"/>
<Button x:Name="MergeGreenSelector" Content="G" Click="MergeGreenSelector_Click" DockPanel.Dock="Left"/>
<Button x:Name="MergeBlueSelector" Content="B" Click="MergeBlueSelector_Click" DockPanel.Dock="Left"/>
<Button x:Name="MergeAlphaSelector" Content="A" Click="MergeAlphaSelector_Click" DockPanel.Dock="Left"/>
<Button x:Name="MergeDeselector" Content="X" Foreground="Red" BorderBrush="Red" BorderThickness="0.5" DockPanel.Dock="Left" Click="MergeDeselector_Click"/>
</DockPanel>
</DockPanel>
</ItemContainerTemplate>
</ListBox.ItemTemplate>
<ListBox.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel MaxWidth="{Binding ElementName=MergeChannelPanel, Path=ActualWidth}"/>
</ItemsPanelTemplate>
</ListBox.ItemsPanel>
</ListBox>
</DockPanel>
</Border>
</Border>
</Grid>
</Window>
Loading

0 comments on commit f833599

Please sign in to comment.