简体   繁体   中英

UWP Apply Colorfilter to BitmapImage

How can I easly apply a ColorFilter to a Bitmap, Image, whatsoever. On Android this is a one-liner: myDrawable.setColorFilter(Color.GRAY, Mode.SRC_IN); For Windows I only found giant over the top compilcated samples to manipulate images in every way. That is way too much for my needs. I don't want to manipulate every pixel at once or similar, I just have a Image with a white Icon and i want that to become green or else programmatically.

您可以使用新的Composition API对任何可视化相当容易地执行此操作 - 您可以在此处查看此代码https://github.com/Microsoft/WindowsUIDevLabs/blob/master/Demos/EffectEditor/MainPage.xaml.cs (请参阅色调/色调效果)

After much time spent wandering around MSDN and S/O, I pieced together the following class. It doesn't use WriteableBitmapEx or any library beyond what is already provided by Microsoft.

There is technically only one blend mode available with this answer; SRC_ATOP. This will iterate over each pixel and replace the colour value with the specified tint colour if the pixel is not transparent (alpha 0).

As noted in many of the answers I've seen; this will be quite slow and not recommended for anything other than applying a one-time tint (ie on start-up of your app). If the colour will not be changing frequently, you may wish to save the result to a local file rather than apply the tint each time.

using System;
using System.IO;
using System.Runtime.InteropServices.WindowsRuntime;
using System.Threading.Tasks;
using Windows.Graphics.Imaging;
using Windows.Storage;
using Windows.Storage.Streams;
using Windows.UI;
using Windows.UI.Xaml.Media.Imaging;

namespace Helpers
{
    public class ImageManipulationHelper
    {

        public static async Task<WriteableBitmap> ApplyTint(Uri sourceUri, Color tintColour)
        {
            WriteableBitmap source = await GetImageFile(sourceUri);
            byte[] byteArray = null;
            using (Stream stream = source.PixelBuffer.AsStream())
            {
                long streamLength = stream.Length;
                byteArray = new byte[streamLength];
                await stream.ReadAsync(byteArray, 0, byteArray.Length);
                if (streamLength > 0)
                {
                    for (int i = 0; i < streamLength; i += 4)
                    {
                        // check the pixel is not transparent (BGRA)
                        if (byteArray[i + 3] != 0)
                        {
                            byteArray[i] = tintColour.B; // Blue
                            byteArray[i + 1] = tintColour.G;  // Green
                            byteArray[i + 2] = tintColour.R; // Red
                        }
                    }
                }   
            }
            if (byteArray != null)
            {
                WriteableBitmap destination = await PixelBufferToWriteableBitmap(byteArray, source.PixelWidth, source.PixelHeight);
                return destination;
            }
            return null;
        }

        private static async Task<WriteableBitmap> GetImageFile(Uri fileUri)
        {
            StorageFile imageFile = await StorageFile.GetFileFromApplicationUriAsync(fileUri);

            WriteableBitmap writeableBitmap = null;
            using (IRandomAccessStream imageStream = await imageFile.OpenReadAsync())
            {
                BitmapDecoder bitmapDecoder = await BitmapDecoder.CreateAsync(imageStream);

                BitmapTransform dummyTransform = new BitmapTransform();
                PixelDataProvider pixelDataProvider =
                   await bitmapDecoder.GetPixelDataAsync(BitmapPixelFormat.Bgra8,
                   BitmapAlphaMode.Premultiplied, dummyTransform,
                   ExifOrientationMode.RespectExifOrientation,
                   ColorManagementMode.ColorManageToSRgb);
                byte[] pixelData = pixelDataProvider.DetachPixelData();

                writeableBitmap = new WriteableBitmap(
                   (int)bitmapDecoder.OrientedPixelWidth,
                   (int)bitmapDecoder.OrientedPixelHeight);
                using (Stream pixelStream = writeableBitmap.PixelBuffer.AsStream())
                {
                    await pixelStream.WriteAsync(pixelData, 0, pixelData.Length);
                }
            }

            return writeableBitmap;
        }



        public static async Task PixelBufferToWriteableBitmap(WriteableBitmap wb, byte[] bgra)
        {
            using (Stream stream = wb.PixelBuffer.AsStream())
            {
                await stream.WriteAsync(bgra, 0, bgra.Length);
            }
        }

        public static async Task<WriteableBitmap> PixelBufferToWriteableBitmap(byte[] bgra, int width, int height)
        {
            var wb = new WriteableBitmap(width, height);
            await PixelBufferToWriteableBitmap(wb, bgra);
            return wb;
        }

    }
}

Happy Coding ^_^

References:

WriteableBitmap to byte array

Set a pixel in WriteableBitmap

How to create WriteableBitmap from BitmapImage

Although not a direct answer because you asked about a more general case of BitmapImage , it's still worth noting that many times you need such a coloring with icons. If you are actually using a BitmapIcon , you are free to change its Foreground and you'll get that same support you found in Android.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM