using System;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using System.Windows.Controls;
using System.Windows.Threading;
using System.Collections.Generic;
namespace RBG.Helpers
{
public class ImageProcessing
{
///
/// 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)
///
/// The source bitmap
/// The red-channel multiplier
/// The green-channel multiplier
/// The blue-channel multiplier
/// The alpha-channel multiplier
///
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;
}
///
/// Retrieves a cropped area of the provided bitmap
///
///
///
///
///
///
///
///
///
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);
}
}
public class AnimatedBitmapImage
{
public BitmapSource BaseImage { get; set; }
BitmapFrame[] frames;
int framerate;
int currentFrame;
Timer frameChange;
public AnimatedBitmapImage(Stream source, Image target, Dispatcher dispatcher)
{
GifBitmapDecoder decoder = new(source, BitmapCreateOptions.None, BitmapCacheOption.Default);
frames = new BitmapFrame[decoder.Frames.Count];
decoder.Frames.CopyTo(frames, 0);
byte[] frameratebytes = new byte[2];
source.Position = 0x324;
frameratebytes[0] = (byte)source.ReadByte();
frameratebytes[1] = (byte)source.ReadByte();
framerate = 33;
BaseImage = frames[0];
// cycle through frames
frameChange = new Timer((t) =>
{
currentFrame++;
currentFrame %= frames.Length;
BaseImage = frames[currentFrame];
_ = dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Render, new Action(() =>
{
try
{
target.Source = BaseImage;
}
catch
{
}
}));
},null, framerate, framerate);
}
}
}