Skip to main content

WPF C#/VB
Making a Simple Photo Editor



C#

// AZUL CODING ---------------------------------------
// WPF C#/VB - Making a Simple Photo Editor
// https://youtu.be/9Gc8BgZl75I


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Imaging;
using System.Drawing;
using System.Drawing.Imaging;

namespace AzulPhotoEditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private readonly Bitmap OriginalImage;
        private Bitmap EditedImage;
        private int CurrentRotation = 0;

        public MainWindow()
        {
            InitializeComponent();

            OriginalImage = new Bitmap(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) + "\\sample.jpg");
            EditedImage = new Bitmap(OriginalImage);
            EditImage.Source = BitmapToSource(new Bitmap(OriginalImage));
        }

        private static BitmapImage BitmapToSource(Bitmap src)
        {
            System.IO.MemoryStream ms = new System.IO.MemoryStream();
            src.Save(ms, ImageFormat.Jpeg);

            BitmapImage image = new BitmapImage();
            image.BeginInit();
            ms.Seek(0, System.IO.SeekOrigin.Begin);
            image.StreamSource = ms;
            image.EndInit();
            return image;
        }

        private void UpdateImage(object sender, EventArgs e)
        {
            if (IsLoaded)
            {
                float brightness = (float)BrightnessSlider.Value;
                float contrast = (float)ContrastSlider.Value;

                if (ContrastSlider.Value < 1)
                {
                    contrast = (float)(ContrastSlider.Value / 2) + 0.5f;
                }

                if (sender is Button btn)
                {
                    if (btn.Name == "RotateAnticlockwiseBtn")
                    {
                        if (CurrentRotation <= 0)
                        {
                            CurrentRotation = 270;
                        }
                        else
                        {
                            CurrentRotation -= 90;
                        }
                    }
                    else if (btn.Name == "RotateClockwiseBtn")
                    {
                        if (CurrentRotation >= 270)
                        {
                            CurrentRotation = 0;
                        }
                        else
                        {
                            CurrentRotation += 90;
                        }
                    }
                }

                float[][] greyscale = new float[][] {
                    new float[] { 0.299f, 0.299f, 0.299f, 0, 0},
                    new float[] { 0.587f, 0.587f, 0.587f, 0, 0},
                    new float[] { 0.114f, 0.114f, 0.114f, 0, 0},
                    new float[] { 0, 0, 0, 1, 0},
                    new float[] { 0, 0, 0, 0, 1}
                };

                float[][] light = new float[][] {
                    new float[] {contrast, 0, 0, 0, 0},
                    new float[] {0, contrast, 0, 0, 0},
                    new float[] {0, 0, contrast, 0, 0},
                    new float[] {0, 0, 0, 1, 0},
                    new float[] {brightness, brightness, brightness, 0, 1}
                };

                Bitmap bmp = new Bitmap(OriginalImage);
                ImageAttributes imgattr = new ImageAttributes();
                Rectangle rc = new Rectangle(0, 0, bmp.Width, bmp.Height);

                if (NoFilterRadio.IsChecked == true)
                {
                    imgattr.SetColorMatrix(new ColorMatrix(light));
                }
                else
                {
                    imgattr.SetColorMatrix(new ColorMatrix(Multiply(greyscale, light)));
                }                

                using (var g = Graphics.FromImage(bmp))
                {
                    g.DrawImage(bmp, rc, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, imgattr);

                    if (RedRadio.IsChecked == true)
                    {
                        g.FillRectangle(new SolidBrush(Color.FromArgb(100, 235, 58, 52)), rc);
                    }
                    else if (GreenRadio.IsChecked == true)
                    {
                        g.FillRectangle(new SolidBrush(Color.FromArgb(100, 52, 235, 73)), rc);
                    }
                    else if (BlueRadio.IsChecked == true)
                    {
                        g.FillRectangle(new SolidBrush(Color.FromArgb(100, 52, 122, 235)), rc);
                    }
                }

                switch (CurrentRotation)
                {
                    case 90:
                        bmp.RotateFlip(RotateFlipType.Rotate90FlipNone);
                        break;
                    case 180:
                        bmp.RotateFlip(RotateFlipType.Rotate180FlipNone);
                        break;
                    case 270:
                        bmp.RotateFlip(RotateFlipType.Rotate270FlipNone);
                        break;
                    default:
                        break;
                }

                EditedImage = bmp;
                EditImage.Source = BitmapToSource(EditedImage);
            }
        }

        private float[][] Multiply(float[][] f1, float[][] f2)
        {
            float[][] X = new float[5][];
            for (int d = 0; d < 5; d++)
                X[d] = new float[5];
            int size = 5;
            float[] column = new float[5];
            for (int j = 0; j < 5; j++)
            {
                for (int k = 0; k < 5; k++)
                {
                    column[k] = f1[k][j];
                }
                for (int i = 0; i < 5; i++)
                {
                    float[] row = f2[i];
                    float s = 0;
                    for (int k = 0; k < size; k++)
                    {
                        s += row[k] * column[k];
                    }
                    X[i][j] = s;
                }
            }
            return X;
        }

        private void ExportBtn_Click(object sender, RoutedEventArgs e)
        {
            EditedImage.Save(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) + "\\sample-edited.jpg");
            MessageBox.Show("Image saved.");
        }
    }
}

Enjoying this tutorial?


VB.NET

' AZUL CODING ---------------------------------------
' WPF C#/VB - Making a Simple Photo Editor
' https://youtu.be/9Gc8BgZl75I


Imports System.Drawing
Imports System.Drawing.Imaging

Class MainWindow

    Private ReadOnly OriginalImage As Bitmap
    Private EditedImage As Bitmap
    Private CurrentRotation As Integer = 0

    Public Sub New()
        InitializeComponent()

        OriginalImage = New Bitmap(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) & "\sample.jpg")
        EditedImage = New Bitmap(OriginalImage)
        EditImage.Source = BitmapToSource(New Bitmap(OriginalImage))
    End Sub

    Private Shared Function BitmapToSource(ByVal src As Bitmap) As BitmapImage
        Dim ms As System.IO.MemoryStream = New System.IO.MemoryStream()
        src.Save(ms, ImageFormat.Jpeg)

        Dim image As BitmapImage = New BitmapImage()
        image.BeginInit()
        ms.Seek(0, System.IO.SeekOrigin.Begin)
        image.StreamSource = ms
        image.EndInit()
        Return image
    End Function

    Private Sub UpdateImage(ByVal sender As Object, ByVal e As EventArgs)
        If IsLoaded Then
            Dim brightness As Single = BrightnessSlider.Value
            Dim contrast As Single = ContrastSlider.Value

            If ContrastSlider.Value < 1 Then
                contrast = (ContrastSlider.Value / 2) + 0.5F
            End If

            If TryCast(sender, Button) IsNot Nothing Then
                Dim btn = TryCast(sender, Button)

                If btn.Name = "RotateAnticlockwiseBtn" Then

                    If CurrentRotation <= 0 Then
                        CurrentRotation = 270
                    Else
                        CurrentRotation -= 90
                    End If

                ElseIf btn.Name = "RotateClockwiseBtn" Then

                    If CurrentRotation >= 270 Then
                        CurrentRotation = 0
                    Else
                        CurrentRotation += 90
                    End If

                End If
            End If

            Dim greyscale As Single()() = New Single()() {
                New Single() {0.299F, 0.299F, 0.299F, 0, 0},
                New Single() {0.587F, 0.587F, 0.587F, 0, 0}, 
                New Single() {0.114F, 0.114F, 0.114F, 0, 0}, 
                New Single() {0, 0, 0, 1, 0}, 
                New Single() {0, 0, 0, 0, 1}}

            Dim light As Single()() = New Single()() {
                New Single() {contrast, 0, 0, 0, 0}, 
                New Single() {0, contrast, 0, 0, 0}, 
                New Single() {0, 0, contrast, 0, 0}, 
                New Single() {0, 0, 0, 1, 0}, 
                New Single() {brightness, brightness, brightness, 0, 1}}

            Dim bmp As Bitmap = New Bitmap(OriginalImage)
            Dim imgattr As ImageAttributes = New ImageAttributes()
            Dim rc As Rectangle = New Rectangle(0, 0, bmp.Width, bmp.Height)

            If NoFilterRadio.IsChecked = True Then
                imgattr.SetColorMatrix(New ColorMatrix(light))
            Else
                imgattr.SetColorMatrix(New ColorMatrix(Multiply(greyscale, light)))
            End If

            Using g = Graphics.FromImage(bmp)
                g.DrawImage(bmp, rc, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, imgattr)

                If RedRadio.IsChecked = True Then
                    g.FillRectangle(New SolidBrush(Color.FromArgb(100, 235, 58, 52)), rc)
                ElseIf GreenRadio.IsChecked = True Then
                    g.FillRectangle(New SolidBrush(Color.FromArgb(100, 52, 235, 73)), rc)
                ElseIf BlueRadio.IsChecked = True Then
                    g.FillRectangle(New SolidBrush(Color.FromArgb(100, 52, 122, 235)), rc)
                End If
            End Using

            Select Case CurrentRotation
                Case 90
                    bmp.RotateFlip(RotateFlipType.Rotate90FlipNone)
                Case 180
                    bmp.RotateFlip(RotateFlipType.Rotate180FlipNone)
                Case 270
                    bmp.RotateFlip(RotateFlipType.Rotate270FlipNone)
                Case Else
            End Select

            EditedImage = bmp
            EditImage.Source = BitmapToSource(EditedImage)
        End If
    End Sub

    Private Function Multiply(ByVal f1 As Single()(), ByVal f2 As Single()()) As Single()()
        Dim X As Single()() = New Single(4)() {}

        For d As Integer = 0 To 5 - 1
            X(d) = New Single(4) {}
        Next

        Dim size As Integer = 5
        Dim column As Single() = New Single(4) {}

        For j As Integer = 0 To 5 - 1

            For k As Integer = 0 To 5 - 1
                column(k) = f1(k)(j)
            Next

            For i As Integer = 0 To 5 - 1
                Dim row As Single() = f2(i)
                Dim s As Single = 0

                For k As Integer = 0 To size - 1
                    s += row(k) * column(k)
                Next

                X(i)(j) = s
            Next
        Next

        Return X
    End Function

    Private Sub ExportBtn_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        EditedImage.Save(Environment.GetFolderPath(Environment.SpecialFolder.MyPictures) & "\sample-edited.jpg")
        MessageBox.Show("Image saved.")
    End Sub

End Class

XAML

<!-- AZUL CODING --------------------------------------- -->
<!-- WPF C#/VB - Making a Simple Photo Editor -->
<!-- https://youtu.be/9Gc8BgZl75I -->


<Window x:Class="AzulPhotoEditor.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:AzulPhotoEditor"
        mc:Ignorable="d"
        Title="Photo Editor - Azul Coding" Height="475" Width="825" ResizeMode="CanMinimize">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition MinWidth="450"/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Grid Margin="20,20,0,20" Grid.Column="0">
            <Image x:Name="EditImage"/>
        </Grid>
        <StackPanel Grid.Column="1" Margin="20">
            <Label Content="Brightness" FontSize="14"/>
            <Slider x:Name="BrightnessSlider" ValueChanged="UpdateImage" VerticalAlignment="Center" Value="0" IsSnapToTickEnabled="True" Minimum="-0.5" Maximum="0.5" LargeChange="0.1" SmallChange="0.01" TickFrequency="0.01" Margin="0,5,0,0"/>
            <Label Content="Contrast" FontSize="14" Margin="0,20,0,0"/>
            <Slider x:Name="ContrastSlider" ValueChanged="UpdateImage" VerticalAlignment="Center" Value="1" IsSnapToTickEnabled="True" Minimum="0" Maximum="2" LargeChange="0.2" TickFrequency="0.02" SmallChange="0.02" Margin="0,5,0,0"/>
            <Label Content="Colour filter" FontSize="14" Margin="0,20,0,0"/>
            <StackPanel Orientation="Horizontal" Margin="10,0,0,0">
                <Grid Margin="0,0,5,0">
                    <RadioButton Name="NoFilterRadio" Content="None" GroupName="FilterRadios" Margin="5" IsChecked="True" Click="UpdateImage"/>
                </Grid>
                <Grid Background="#64999999" Margin="0,0,5,0">
                    <RadioButton Name="GrayscaleRadio" Content="Gray" GroupName="FilterRadios" Margin="5" Click="UpdateImage"/>
                </Grid>
                <Grid Background="#64eb3a34" Margin="0,0,5,0">
                    <RadioButton Name="RedRadio" Content="Red" GroupName="FilterRadios" Margin="5" Click="UpdateImage"/>
                </Grid>
                <Grid Background="#6434eb49" Margin="0,0,5,0">
                    <RadioButton Name="GreenRadio" Content="Green" GroupName="FilterRadios" Margin="5" Click="UpdateImage"/>
                </Grid>
                <Grid Background="#64347aeb">
                    <RadioButton Name="BlueRadio" Content="Blue" GroupName="FilterRadios" Margin="5" Click="UpdateImage"/>
                </Grid>
            </StackPanel>
            <Label Content="Rotation" FontSize="14" Margin="0,20,0,0"/>
            <Button Name="RotateAnticlockwiseBtn" Content="Rotate 90° anticlockwise" HorizontalAlignment="Left" Padding="20,5" Margin="10,5,0,0" Click="UpdateImage"/>
            <Button Name="RotateClockwiseBtn" Content="Rotate 90° clockwise" HorizontalAlignment="Left" Padding="20,5" Margin="10,5,0,0" Click="UpdateImage"/>
            <Label Content="Export edited image" FontSize="14" Margin="0,20,0,0"/>
            <Button Name="ExportBtn" Content="Export" HorizontalAlignment="Left" Padding="20,5" Margin="10,5,0,0" Click="ExportBtn_Click"/>
        </StackPanel>
    </Grid>
</Window>