how to make image only color of black or white in .net maui?

mc 6,236 Reputation points
2025-11-20T04:33:33.7866667+00:00

I am using .net maui and I want to use slider to change the value of gray and then the picture will change.

use SKImage or Microsoft.Graphics.Image how to transform the colors to black and white only?

the pixels is too much and I do not want to use code that below:

for(int i=0;i<img.Width;i++){
	for(int j=0;j<img.Height;j++){
	//change pixel color.
	}
}

what I want is that when I scroll the slider the Bitmap will transform to colors black and white according the value.

Developer technologies | .NET | .NET MAUI
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Jack Dang (WICLOUD CORPORATION) 3,965 Reputation points Microsoft External Staff Moderator
    2025-11-20T11:01:57.67+00:00

    Hi @mc ,

    Thanks for reaching out.

    I understand that you want to apply a slider-controlled black-and-white effect to your images in a .NET MAUI app, while also being able to switch between multiple images at runtime. Below is a complete working solution using SkiaSharp and embedded resources. This approach allows your images to be displayed in the app with a slider controlling the effect from full color → grayscale → hard black-and-white.

    MainPage.xaml

    <?xml version="1.0" encoding="utf-8" ?>
    <ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
                 xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                 xmlns:skia="clr-namespace:SkiaSharp.Views.Maui.Controls;assembly=SkiaSharp.Views.Maui.Controls"
                 x:Class="MauiBWApp.MainPage">
        <VerticalStackLayout Padding="20" Spacing="15">
            <!-- SKCanvas -->
            <skia:SKCanvasView x:Name="CanvasView"
                               HeightRequest="400"
                               PaintSurface="CanvasView_PaintSurface" />
            <!-- Slider -->
            <Slider x:Name="ThresholdSlider"
                    Minimum="0"
                    Maximum="1"
                    Value="0.0"
                    ValueChanged="ThresholdSlider_ValueChanged" />
            <!-- Slider Label -->
            <Label x:Name="ThresholdLabel"
                   HorizontalOptions="Center" />
            <!-- Image name label -->
            <Label x:Name="ImageNameLabel"
                   HorizontalOptions="Center"
                   FontAttributes="Bold"
                   FontSize="Medium" />
            <!-- Buttons -->
            <HorizontalStackLayout Spacing="10" HorizontalOptions="Center">
                <Button Text="Previous" Clicked="PreviousImageButton_Clicked" />
                <Button Text="Next" Clicked="NextImageButton_Clicked" />
            </HorizontalStackLayout>
        </VerticalStackLayout>
    </ContentPage>
    

    MainPage.xaml.cs

    using Microsoft.Maui.Controls;
    using SkiaSharp;
    using SkiaSharp.Views.Maui;
    using SkiaSharp.Views.Maui.Controls;
    using System.Reflection;
    namespace MauiBWApp;
    
    public partial class MainPage : ContentPage
    {
        private SKBitmap? _originalBitmap;
        private float _sliderValue = 0.0f;
        private string[] _imageResources = Array.Empty<string>();
        private int _currentImageIndex = 0;
        public MainPage()
        {
            InitializeComponent();
            ThresholdLabel.Text = $"Slider: {_sliderValue:F2}";
            // Detect all embedded PNG images in Resources/Images
            var assembly = GetType().Assembly;
            _imageResources = assembly.GetManifestResourceNames()
                                      .Where(r => r.EndsWith(".png", StringComparison.OrdinalIgnoreCase) &&
                                                  r.Contains("Resources.Images"))
                                      .ToArray();
            if (_imageResources.Length > 0)
            {
                LoadCurrentImage();
            }
            else
            {
                System.Diagnostics.Debug.WriteLine("No embedded images found in Resources/Images!");
            }
        }
        private Task LoadBitmapFromResource(string resourceName)
        {
            try
            {
                var assembly = GetType().Assembly;
                using Stream? stream = assembly.GetManifestResourceStream(resourceName);
                if (stream == null)
                {
                    System.Diagnostics.Debug.WriteLine($"STREAM IS NULL - check resource path: {resourceName}");
                    return Task.CompletedTask;
                }
                _originalBitmap = SKBitmap.Decode(stream);
                CanvasView.InvalidateSurface();
                // Show image name
                var parts = resourceName.Split('.');
                string shortName = parts[parts.Length - 2]; 
                ImageNameLabel.Text = shortName;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine($"Error loading image: {ex.Message}");
            }
            return Task.CompletedTask;
        }
    
        private void LoadCurrentImage()
        {
            if (_imageResources.Length == 0) return;
            _ = LoadBitmapFromResource(_imageResources[_currentImageIndex]);
        }
    
        private void ThresholdSlider_ValueChanged(object sender, ValueChangedEventArgs e)
        {
            _sliderValue = (float)e.NewValue;
            ThresholdLabel.Text = $"Slider: {_sliderValue:F2}";
            CanvasView.InvalidateSurface();
        }
    
        private void NextImageButton_Clicked(object sender, EventArgs e)
        {
            if (_imageResources.Length == 0) return;
            _currentImageIndex = (_currentImageIndex + 1) % _imageResources.Length;
            LoadCurrentImage();
        }
    
        private void PreviousImageButton_Clicked(object sender, EventArgs e)
        {
            if (_imageResources.Length == 0) return;
            _currentImageIndex = (_currentImageIndex - 1 + _imageResources.Length) % _imageResources.Length;
            LoadCurrentImage();
        }
    
        private void CanvasView_PaintSurface(object sender, SKPaintSurfaceEventArgs e)
        {
            var canvas = e.Surface.Canvas;
            canvas.Clear(SKColors.White);
            if (_originalBitmap == null)
                return;
            using var paint = new SKPaint();
            float t = _sliderValue;
            float[] matrix;
            // Slider effect: 0 → full color, 0.99 → grayscale, 1 → hard black-and-white
            if (t <= 0.99f)
            {
                float alpha = t / 0.99f;
                matrix = new float[]
                {
                    1 - alpha + alpha * 0.2126f, alpha * 0.7152f, alpha * 0.0722f, 0, 0,
                    alpha * 0.2126f, 1 - alpha + alpha * 0.7152f, alpha * 0.0722f, 0, 0,
                    alpha * 0.2126f, alpha * 0.7152f, 1 - alpha + alpha * 0.0722f, 0, 0,
                    0,0,0,1,0
                };
            }
            else
            {
                float alpha = (t - 0.99f) / 0.01f;
                float bias = -128 * alpha;
                matrix = new float[]
                {
                    0.2126f, 0.7152f, 0.0722f, 0, bias,
                    0.2126f, 0.7152f, 0.0722f, 0, bias,
                    0.2126f, 0.7152f, 0.0722f, 0, bias,
                    0,0,0,1,0
                };
            }
    
            paint.ColorFilter = SKColorFilter.CreateColorMatrix(matrix);
            // Preserve aspect ratio
            float canvasWidth = e.Info.Width;
            float canvasHeight = e.Info.Height;
            float imgWidth = _originalBitmap.Width;
            float imgHeight = _originalBitmap.Height;
            float scale = Math.Min(canvasWidth / imgWidth, canvasHeight / imgHeight);
            float width = imgWidth * scale;
            float height = imgHeight * scale;
            float x = (canvasWidth - width) / 2;
            float y = (canvasHeight - height) / 2;
            var destRect = new SKRect(x, y, x + width, y + height);
            canvas.DrawBitmap(_originalBitmap, destRect, paint);
        }
    }
    

    MauiProgram.cs

    using Microsoft.Extensions.Logging;
    using SkiaSharp.Views.Maui.Controls.Hosting;
    namespace MauiBWApp;
    
    public static class MauiProgram
    {
        public static MauiApp CreateMauiApp()
        {
            var builder = MauiApp.CreateBuilder();
            builder
                .UseMauiApp<App>()
                .UseSkiaSharp()  // registers SkiaSharp handlers
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
                });
    #if DEBUG
            builder.Logging.AddDebug();
    #endif
            return builder.Build();
        }
    }
    

    Key Notes

    1. Images as Embedded Resources for SkiaSharp:
    • By default, images in Resources/Images have Build Action: MauiImage, which works for standard MAUI Image controls but cannot be loaded via SkiaSharp.
    • To use SkiaSharp, you need to embed them in the assembly. You can do this in your .csproj:
    <ItemGroup>
      <EmbeddedResource Include="Resources\Images\**\*.png" />
    </ItemGroup>
    
    • If you previously had any <EmbeddedResource Remove="…"/> entries for these images, comment them out or delete them, otherwise SkiaSharp won’t see the images.
    • Alternatively, you can place SkiaSharp images in a separate folder (e.g., Assets/ImagesForSkia) to avoid touching the Resources/Images folder and keep it for normal MAUI images.
    1. Slider behavior:
      • 0 → original full color
      • 0.99 → grayscale
      • 1 → hard black-and-white (threshold)
    2. Dynamic image switching:
      • Next/Previous buttons cycle through all embedded PNG images.
    3. SkiaSharp handles rendering:
      • The color matrix automatically adjusts the image based on the slider value, giving a smooth transition from color → grayscale → black-and-white.

    Hope this helps! If my answer was helpful - kindly follow the instructions here so others with the same problem can benefit as well.

    0 comments No comments

Your answer

Answers can be marked as 'Accepted' by the question author and 'Recommended' by moderators, which helps users know the answer solved the author's problem.