87 lines
4.3 KiB
C#
87 lines
4.3 KiB
C#
using System;
|
|
using System.Windows;
|
|
using System.Windows.Media.Imaging;
|
|
|
|
namespace RBG.Helpers
|
|
{
|
|
public class ImageProcessing
|
|
{
|
|
/// <summary>
|
|
/// Returns a copy of the original bitmap with a specified per-channel colour multiplier
|
|
/// Each channel expects a rate 0-1 (will handle other values, with unexpected results)
|
|
/// Defaults to 1.0 for each channel (no modification)
|
|
/// </summary>
|
|
/// <param name="sourceBitmap">The source bitmap</param>
|
|
/// <param name="redRate">The red-channel multiplier</param>
|
|
/// <param name="greenRate">The green-channel multiplier</param>
|
|
/// <param name="blueRate">The blue-channel multiplier</param>
|
|
/// <param name="alphaRate">The alpha-channel multiplier</param>
|
|
/// <returns></returns>
|
|
public static BitmapSource UpdatePixelColours(BitmapSource sourceBitmap, double redRate = 1.0, double greenRate = 1.0, double blueRate = 1.0, double alphaRate = 1.0)
|
|
{
|
|
// Create a writeable bitmap from the source bitmap
|
|
WriteableBitmap wbmp = new(sourceBitmap);
|
|
// Ensure 32 bpp
|
|
if (wbmp.Format.BitsPerPixel != 32) return null;
|
|
// Lock the bitmap so the buffer cannot change
|
|
wbmp.Lock();
|
|
// Enter unsafe code (used over marshalled pointer access so that it is more performant, but has a
|
|
// higher security risk as this is raw [but still bounded] memory access ( => data may be changed by a third party)
|
|
// As the buffer is of a known size, and not terminated by a specific symbol, modified data in the buffer is
|
|
// unlikely to cause a novel vulnerability in the software ( => vulnerability exists in BitmapSource, can be
|
|
// exploited with any bitmap image being loaded)
|
|
unsafe
|
|
{
|
|
// Retrieve the buffer pointer
|
|
int* arrayptr = (int*)wbmp.BackBuffer;
|
|
// Iterate through each pixel
|
|
for (int i = 0; i < wbmp.PixelWidth * wbmp.PixelHeight; i++)
|
|
{
|
|
// Dereference the exact pixel value
|
|
int pixel = *(arrayptr + i);
|
|
// decompose the colour channels
|
|
int a = (pixel >> 24) & 0xFF;
|
|
int r = (pixel >> 16) & 0xFF;
|
|
int g = (pixel >> 8) & 0xFF;
|
|
int b = pixel & 0xFF;
|
|
// Calc new values & clamp to a max of 0xFF (8 bits per channel)
|
|
int newB = (int)Math.Min((uint)(b * blueRate), 255);
|
|
int newG = (int)Math.Min((uint)(g * greenRate), 255);
|
|
int newR = (int)Math.Min((uint)(r * redRate), 255);
|
|
int newA = (int)Math.Min((uint)(a * alphaRate), 255);
|
|
// Set the pixel to the new value
|
|
*(arrayptr + i) = (newA << 24) | (newR << 16) | (newG << 8) | newB;
|
|
}
|
|
}
|
|
// Invalidate the full current image, so that the new buffer data is fully applied
|
|
wbmp.AddDirtyRect(new Int32Rect(0, 0, wbmp.PixelWidth, wbmp.PixelHeight));
|
|
// Unlock the image, as everything has finished changing
|
|
wbmp.Unlock();
|
|
return wbmp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a cropped area of the provided bitmap
|
|
/// </summary>
|
|
/// <param name="source"></param>
|
|
/// <param name="row"></param>
|
|
/// <param name="col"></param>
|
|
/// <param name="rowSize"></param>
|
|
/// <param name="colSize"></param>
|
|
/// <param name="colsIncluded"></param>
|
|
/// <param name="rowsIncluded"></param>
|
|
/// <returns></returns>
|
|
public static BitmapSource RetrieveSubImage(BitmapSource source, int row, int col, int rowSize, int colSize, int colsIncluded = 1, int rowsIncluded = 1)
|
|
{
|
|
// Get number of pixels per segment
|
|
int rowPixelsPerTile = (source.PixelWidth / rowSize) - 1;
|
|
int colPixelsPerTile = (source.PixelHeight / colSize) - 1;
|
|
// Create a cropping bound
|
|
Int32Rect pixelRect = new(colPixelsPerTile * col, rowPixelsPerTile * row, colPixelsPerTile * colsIncluded, rowPixelsPerTile * rowsIncluded);
|
|
return new CroppedBitmap(source, pixelRect);
|
|
}
|
|
|
|
|
|
}
|
|
}
|