/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2013 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on Sourceforge: http://sourceforge.net/projects/greenshot/
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
namespace GreenshotPlugin.Core
{
///
/// The interface for the FastBitmap
///
public interface IFastBitmap : IDisposable
{
///
/// Get the color at x,y
/// The returned Color object depends on the underlying pixel format
///
/// int x
/// int y
/// Color
Color GetColorAt(int x, int y);
///
/// Set the color at the specified location
///
/// int x
/// int y
/// Color
void SetColorAt(int x, int y, Color color);
///
/// Get the color at x,y
/// The returned byte[] color depends on the underlying pixel format
///
/// int x
/// int y
/// Set the color at the specified location
///
/// int x
/// int y
/// byte[] color
void SetColorAt(int x, int y, byte[] color);
///
/// Lock the bitmap
///
void Lock();
///
/// Unlock the bitmap
///
void Unlock();
///
/// Unlock the bitmap and get the underlying bitmap in one call
///
///
Bitmap UnlockAndReturnBitmap();
///
/// Size of the underlying image
///
Size Size
{
get;
}
///
/// Height of the image area that this fastbitmap covers
///
int Height
{
get;
}
///
/// Width of the image area that this fastbitmap covers
///
int Width
{
get;
}
///
/// Top of the image area that this fastbitmap covers
///
int Top
{
get;
}
///
/// Left of the image area that this fastbitmap covers
///
int Left
{
get;
}
///
/// Right of the image area that this fastbitmap covers
///
int Right
{
get;
}
///
/// Bottom of the image area that this fastbitmap covers
///
int Bottom
{
get;
}
///
/// Does the underlying image need to be disposed
///
bool NeedsDispose
{
get;
set;
}
///
/// Returns if this FastBitmap has an alpha channel
///
bool hasAlphaChannel
{
get;
}
///
/// Draw the stored bitmap to the destionation bitmap at the supplied point
///
/// Graphics
/// Point with location
void DrawTo(Graphics graphics, Point destination);
///
/// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
/// Be aware that the stored bitmap will be resized to the specified rectangle!!
///
/// Graphics
/// Rectangle with destination
void DrawTo(Graphics graphics, Rectangle destinationRect);
///
/// Return true if the coordinates are inside the FastBitmap
///
///
///
///
bool Contains(int x, int y);
///
/// Set the bitmap resolution
///
///
///
void SetResolution(float horizontal, float vertical);
}
///
/// This interface can be used for when offsetting is needed
///
public interface IFastBitmapWithOffset : IFastBitmap
{
///
/// Return true if the coordinates are inside the FastBitmap
///
///
///
///
new bool Contains(int x, int y);
///
/// Set the color at the specified location, using offsetting so the original coordinates can be used
///
/// int x
/// int y
/// Color color
new void SetColorAt(int x, int y, Color color);
///
/// Set the color at the specified location, using offsetting so the original coordinates can be used
///
/// int x
/// int y
/// byte[] color
new void SetColorAt(int x, int y, byte[] color);
///
/// Get the color at x,y
/// The returned Color object depends on the underlying pixel format
///
/// int x
/// int y
/// Color
new Color GetColorAt(int x, int y);
///
/// Get the color at x,y, using offsetting so the original coordinates can be used
/// The returned byte[] color depends on the underlying pixel format
///
/// int x
/// int y
/// This interface can be used for when clipping is needed
///
public interface IFastBitmapWithClip : IFastBitmap
{
Rectangle Clip
{
get;
set;
}
bool InvertClip
{
get;
set;
}
///
/// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
///
/// int x
/// int y
/// Color color
new void SetColorAt(int x, int y, Color color);
///
/// Set the color at the specified location, this doesn't do anything if the location is excluded due to clipping
///
/// int x
/// int y
/// byte[] color
new void SetColorAt(int x, int y, byte[] color);
///
/// Return true if the coordinates are inside the FastBitmap and not clipped
///
///
///
///
new bool Contains(int x, int y);
}
///
/// This interface is implemented when there is a alpha-blending possibility
///
public interface IFastBitmapWithBlend : IFastBitmap
{
Color BackgroundBlendColor
{
get;
set;
}
Color GetBlendedColorAt(int x, int y);
}
///
/// The base class for the fast bitmap implementation
///
public unsafe abstract class FastBitmap : IFastBitmap, IFastBitmapWithClip, IFastBitmapWithOffset
{
protected const int PIXELFORMAT_INDEX_A = 3;
protected const int PIXELFORMAT_INDEX_R = 2;
protected const int PIXELFORMAT_INDEX_G = 1;
protected const int PIXELFORMAT_INDEX_B = 0;
public const int COLOR_INDEX_R = 0;
public const int COLOR_INDEX_G = 1;
public const int COLOR_INDEX_B = 2;
public const int COLOR_INDEX_A = 3;
protected Rectangle area = Rectangle.Empty;
///
/// If this is set to true, the bitmap will be disposed when disposing the IFastBitmap
///
public bool NeedsDispose
{
get;
set;
}
public Rectangle Clip
{
get;
set;
}
public bool InvertClip
{
get;
set;
}
///
/// The bitmap for which the FastBitmap is creating access
///
protected Bitmap bitmap;
protected BitmapData bmData;
protected int stride; /* bytes per pixel row */
protected bool bitsLocked = false;
protected byte* pointer;
public static IFastBitmap Create(Bitmap source)
{
return Create(source, Rectangle.Empty);
}
public void SetResolution(float horizontal, float vertical)
{
bitmap.SetResolution(horizontal, vertical);
}
///
/// Factory for creating a FastBitmap depending on the pixelformat of the source
/// The supplied rectangle specifies the area for which the FastBitmap does its thing
///
/// Bitmap to access
/// Rectangle which specifies the area to have access to, can be Rectangle.Empty for the whole image
/// IFastBitmap
public static IFastBitmap Create(Bitmap source, Rectangle area)
{
switch (source.PixelFormat)
{
case PixelFormat.Format8bppIndexed:
return new FastChunkyBitmap(source, area);
case PixelFormat.Format24bppRgb:
return new Fast24RGBBitmap(source, area);
case PixelFormat.Format32bppRgb:
return new Fast32RGBBitmap(source, area);
case PixelFormat.Format32bppArgb:
case PixelFormat.Format32bppPArgb:
return new Fast32ARGBBitmap(source, area);
default:
throw new NotSupportedException(string.Format("Not supported Pixelformat {0}", source.PixelFormat));
}
}
///
/// Factory for creating a FastBitmap as a destination for the source
///
/// Bitmap to clone
/// IFastBitmap
public static IFastBitmap CreateCloneOf(Image source)
{
return CreateCloneOf(source, source.PixelFormat, Rectangle.Empty);
}
///
/// Factory for creating a FastBitmap as a destination for the source
///
/// Bitmap to clone
/// new Pixelformat
/// IFastBitmap
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat)
{
return CreateCloneOf(source, pixelFormat, Rectangle.Empty);
}
///
/// Factory for creating a FastBitmap as a destination for the source
///
/// Bitmap to clone
/// Area of the bitmap to access, can be Rectangle.Empty for the whole
/// IFastBitmap
public static IFastBitmap CreateCloneOf(Image source, Rectangle area)
{
return CreateCloneOf(source, PixelFormat.DontCare, area);
}
///
/// Factory for creating a FastBitmap as a destination for the source
///
/// Bitmap to clone
/// Pixelformat of the cloned bitmap
/// Area of the bitmap to access, can be Rectangle.Empty for the whole
/// IFastBitmap
public static IFastBitmap CreateCloneOf(Image source, PixelFormat pixelFormat, Rectangle area)
{
Bitmap destination = ImageHelper.CloneArea(source, area, pixelFormat);
FastBitmap fastBitmap = Create(destination) as FastBitmap;
fastBitmap.NeedsDispose = true;
fastBitmap.Left = area.Left;
fastBitmap.Top = area.Top;
return fastBitmap;
}
///
/// Factory for creating a FastBitmap as a destination
///
///
///
///
/// IFastBitmap
public static IFastBitmap CreateEmpty(Size newSize, PixelFormat pixelFormat, Color backgroundColor)
{
Bitmap destination = ImageHelper.CreateEmpty(newSize.Width, newSize.Height, pixelFormat, backgroundColor, 96f, 96f);
IFastBitmap fastBitmap = Create(destination);
fastBitmap.NeedsDispose = true;
return fastBitmap;
}
///
/// Constructor which stores the image and locks it when called
///
///
protected FastBitmap(Bitmap bitmap, Rectangle area)
{
this.bitmap = bitmap;
Rectangle bitmapArea = new Rectangle(Point.Empty, bitmap.Size);
if (area != Rectangle.Empty)
{
area.Intersect(bitmapArea);
this.area = area;
}
else
{
this.area = bitmapArea;
}
// As the lock takes care that only the specified area is made available we need to calculate the offset
Left = area.Left;
Top = area.Top;
// Default cliping is done to the area without invert
Clip = this.area;
InvertClip = false;
// Always lock, so we don't need to do this ourselves
Lock();
}
///
/// Return the size of the image
///
public Size Size
{
get
{
if (area == Rectangle.Empty)
{
return bitmap.Size;
}
return area.Size;
}
}
///
/// Return the width of the image
///
public int Width
{
get
{
if (area == Rectangle.Empty)
{
return bitmap.Width;
}
return area.Width;
}
}
///
/// Return the height of the image
///
public int Height
{
get
{
if (area == Rectangle.Empty)
{
return bitmap.Height;
}
return area.Height;
}
}
private int left;
///
/// Return the left of the fastbitmap, this is also used as an offset
///
public int Left
{
get
{
return 0;
}
set
{
left = value;
}
}
///
/// Return the left of the fastbitmap, this is also used as an offset
///
int IFastBitmapWithOffset.Left
{
get
{
return left;
}
set
{
left = value;
}
}
private int top;
///
/// Return the top of the fastbitmap, this is also used as an offset
///
public int Top
{
get
{
return 0;
}
set
{
top = value;
}
}
///
/// Return the top of the fastbitmap, this is also used as an offset
///
int IFastBitmapWithOffset.Top
{
get
{
return top;
}
set
{
top = value;
}
}
///
/// Return the right of the fastbitmap
///
public int Right
{
get
{
return Left + Width;
}
}
///
/// Return the bottom of the fastbitmap
///
public int Bottom
{
get
{
return Top + Height;
}
}
///
/// Returns the underlying bitmap, unlocks it and prevents that it will be disposed
///
public Bitmap UnlockAndReturnBitmap()
{
if (bitsLocked)
{
Unlock();
}
NeedsDispose = false;
return bitmap;
}
public virtual bool hasAlphaChannel
{
get
{
return false;
}
}
///
/// Destructor
///
~FastBitmap()
{
Dispose(false);
}
///
/// The public accessible Dispose
/// Will call the GarbageCollector to SuppressFinalize, preventing being cleaned twice
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
// The bulk of the clean-up code is implemented in Dispose(bool)
///
/// This Dispose is called from the Dispose and the Destructor.
/// When disposing==true all non-managed resources should be freed too!
///
///
protected virtual void Dispose(bool disposing)
{
Unlock();
if (disposing)
{
if (bitmap != null && NeedsDispose)
{
bitmap.Dispose();
}
}
bitmap = null;
bmData = null;
pointer = null;
}
///
/// Lock the bitmap so we have direct access to the memory
///
public void Lock()
{
if (Width > 0 && Height > 0 && !bitsLocked)
{
bmData = bitmap.LockBits(area, ImageLockMode.ReadWrite, bitmap.PixelFormat);
bitsLocked = true;
IntPtr Scan0 = bmData.Scan0;
pointer = (byte*)(void*)Scan0;
stride = bmData.Stride;
}
}
///
/// Unlock the System Memory
///
public void Unlock()
{
if (bitsLocked)
{
bitmap.UnlockBits(bmData);
bitsLocked = false;
}
}
///
/// Draw the stored bitmap to the destionation bitmap at the supplied point
///
///
///
public void DrawTo(Graphics graphics, Point destination)
{
DrawTo(graphics, new Rectangle(destination, area.Size));
}
///
/// Draw the stored Bitmap on the Destination bitmap with the specified rectangle
/// Be aware that the stored bitmap will be resized to the specified rectangle!!
///
///
///
///
public void DrawTo(Graphics graphics, Rectangle destinationRect)
{
// Make sure this.bitmap is unlocked, if it was locked
bool isLocked = bitsLocked;
if (isLocked)
{
Unlock();
}
graphics.DrawImage(bitmap, destinationRect, area, GraphicsUnit.Pixel);
}
///
/// returns true if x & y are inside the FastBitmap
///
///
///
/// true if x & y are inside the FastBitmap
public bool Contains(int x, int y)
{
return area.Contains(x - Left, y - Top);
}
public abstract Color GetColorAt(int x, int y);
public abstract void SetColorAt(int x, int y, Color color);
public abstract void GetColorAt(int x, int y, byte[] color);
public abstract void SetColorAt(int x, int y, byte[] color);
#region IFastBitmapWithClip
bool IFastBitmapWithClip.Contains(int x, int y)
{
bool contains = Clip.Contains(x, y);
if (InvertClip)
{
return !contains;
}
else
{
return contains;
}
}
void IFastBitmapWithClip.SetColorAt(int x, int y, byte[] color)
{
bool contains = Clip.Contains(x, y);
if ((InvertClip && contains) || (!InvertClip && !contains))
{
return;
}
SetColorAt(x, y, color);
}
void IFastBitmapWithClip.SetColorAt(int x, int y, Color color)
{
bool contains = Clip.Contains(x, y);
if ((InvertClip && contains) || (!InvertClip && !contains))
{
return;
}
SetColorAt(x, y, color);
}
#endregion IFastBitmapWithClip
#region IFastBitmapWithOffset
///
/// returns true if x & y are inside the FastBitmap
///
///
///
/// true if x & y are inside the FastBitmap
bool IFastBitmapWithOffset.Contains(int x, int y)
{
return area.Contains(x - Left, y - Top);
}
Color IFastBitmapWithOffset.GetColorAt(int x, int y)
{
x -= left;
y -= top;
return GetColorAt(x, y);
}
void IFastBitmapWithOffset.GetColorAt(int x, int y, byte[] color)
{
x -= left;
y -= top;
GetColorAt(x, y, color);
}
void IFastBitmapWithOffset.SetColorAt(int x, int y, byte[] color)
{
x -= left;
y -= top;
SetColorAt(x, y, color);
}
void IFastBitmapWithOffset.SetColorAt(int x, int y, Color color)
{
x -= left;
y -= top;
SetColorAt(x, y, color);
}
#endregion IFastBitmapWithOffset
}
///
/// This is the implementation of the FastBitmat for the 8BPP pixelformat
///
public unsafe class FastChunkyBitmap : FastBitmap
{
// Used for indexed images
private Color[] colorEntries;
private Dictionary colorCache = new Dictionary();
public FastChunkyBitmap(Bitmap source, Rectangle area)
: base(source, area)
{
colorEntries = bitmap.Palette.Entries;
}
///
/// Get the color from the specified location
///
///
///
/// Color
public override Color GetColorAt(int x, int y)
{
int offset = x + (y * stride);
byte colorIndex = pointer[offset];
return colorEntries[colorIndex];
}
///
/// Get the color from the specified location into the specified array
///
///
///
/// byte[4] as reference
public override void GetColorAt(int x, int y, byte[] color)
{
throw new NotImplementedException("No performance gain!");
}
///
/// Set the color at the specified location from the specified array
///
///
///
/// byte[4] as reference
public override void SetColorAt(int x, int y, byte[] color)
{
throw new NotImplementedException("No performance gain!");
}
///
/// Get the color-index from the specified location
///
///
///
/// byte with index
public byte GetColorIndexAt(int x, int y)
{
int offset = x + (y * stride);
return pointer[offset];
}
///
/// Set the color-index at the specified location
///
///
///
///
public void SetColorIndexAt(int x, int y, byte colorIndex)
{
int offset = x + (y * stride);
pointer[offset] = colorIndex;
}
///
/// Set the supplied color at the specified location.
/// Throws an ArgumentException if the color is not in the palette
///
///
///
/// Color to set
public override void SetColorAt(int x, int y, Color color)
{
int offset = x + (y * stride);
byte colorIndex;
if (!colorCache.TryGetValue(color, out colorIndex))
{
bool foundColor = false;
for (colorIndex = 0; colorIndex < colorEntries.Length; colorIndex++)
{
if (color == colorEntries[colorIndex])
{
colorCache.Add(color, colorIndex);
foundColor = true;
break;
}
}
if (!foundColor)
{
throw new ArgumentException("No such color!");
}
}
pointer[offset] = colorIndex;
}
}
///
/// This is the implementation of the IFastBitmap for 24 bit images (no Alpha)
///
public unsafe class Fast24RGBBitmap : FastBitmap
{
public Fast24RGBBitmap(Bitmap source, Rectangle area)
: base(source, area)
{
}
///
/// Retrieve the color at location x,y
/// Before the first time this is called the Lock() should be called once!
///
/// X coordinate
/// Y Coordinate
/// Color
public override Color GetColorAt(int x, int y)
{
int offset = (x * 3) + (y * stride);
return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]);
}
///
/// Set the color at location x,y
/// Before the first time this is called the Lock() should be called once!
///
///
///
///
public override void SetColorAt(int x, int y, Color color)
{
int offset = (x * 3) + (y * stride);
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
}
///
/// Get the color from the specified location into the specified array
///
///
///
/// byte[4] as reference (r,g,b)
public override void GetColorAt(int x, int y, byte[] color)
{
int offset = (x * 3) + (y * stride);
color[PIXELFORMAT_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
color[PIXELFORMAT_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
color[PIXELFORMAT_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
}
///
/// Set the color at the specified location from the specified array
///
///
///
/// byte[4] as reference (r,g,b)
public override void SetColorAt(int x, int y, byte[] color)
{
int offset = (x * 3) + (y * stride);
pointer[PIXELFORMAT_INDEX_R + offset] = color[PIXELFORMAT_INDEX_R];
pointer[PIXELFORMAT_INDEX_G + offset] = color[PIXELFORMAT_INDEX_G];
pointer[PIXELFORMAT_INDEX_B + offset] = color[PIXELFORMAT_INDEX_B];
}
}
///
/// This is the implementation of the IFastBitmap for 32 bit images (no Alpha)
///
public unsafe class Fast32RGBBitmap : FastBitmap
{
public Fast32RGBBitmap(Bitmap source, Rectangle area)
: base(source, area)
{
}
///
/// Retrieve the color at location x,y
/// Before the first time this is called the Lock() should be called once!
///
/// X coordinate
/// Y Coordinate
/// Color
public override Color GetColorAt(int x, int y)
{
int offset = (x * 4) + (y * stride);
return Color.FromArgb(255, pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]);
}
///
/// Set the color at location x,y
/// Before the first time this is called the Lock() should be called once!
///
///
///
///
public override void SetColorAt(int x, int y, Color color)
{
int offset = (x * 4) + (y * stride);
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
}
///
/// Get the color from the specified location into the specified array
///
///
///
/// byte[4] as reference (a,r,g,b)
public override void GetColorAt(int x, int y, byte[] color)
{
int offset = (x * 4) + (y * stride);
color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
}
///
/// Set the color at the specified location from the specified array
///
///
///
/// byte[4] as reference (r,g,b)
public override void SetColorAt(int x, int y, byte[] color)
{
int offset = (x * 4) + (y * stride);
pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R
pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G];
pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B];
}
}
///
/// This is the implementation of the IFastBitmap for 32 bit images with Alpha
///
public unsafe class Fast32ARGBBitmap : FastBitmap, IFastBitmapWithBlend
{
public override bool hasAlphaChannel
{
get
{
return true;
}
}
public Color BackgroundBlendColor
{
get;
set;
}
public Fast32ARGBBitmap(Bitmap source, Rectangle area)
: base(source, area)
{
BackgroundBlendColor = Color.White;
}
///
/// Retrieve the color at location x,y
///
/// X coordinate
/// Y Coordinate
/// Color
public override Color GetColorAt(int x, int y)
{
int offset = (x * 4) + (y * stride);
return Color.FromArgb(pointer[PIXELFORMAT_INDEX_A + offset], pointer[PIXELFORMAT_INDEX_R + offset], pointer[PIXELFORMAT_INDEX_G + offset], pointer[PIXELFORMAT_INDEX_B + offset]);
}
///
/// Set the color at location x,y
/// Before the first time this is called the Lock() should be called once!
///
///
///
///
public override void SetColorAt(int x, int y, Color color)
{
int offset = (x * 4) + (y * stride);
pointer[PIXELFORMAT_INDEX_A + offset] = color.A;
pointer[PIXELFORMAT_INDEX_R + offset] = color.R;
pointer[PIXELFORMAT_INDEX_G + offset] = color.G;
pointer[PIXELFORMAT_INDEX_B + offset] = color.B;
}
///
/// Get the color from the specified location into the specified array
///
///
///
/// byte[4] as reference (r,g,b,a)
public override void GetColorAt(int x, int y, byte[] color)
{
int offset = (x * 4) + (y * stride);
color[COLOR_INDEX_R] = pointer[PIXELFORMAT_INDEX_R + offset];
color[COLOR_INDEX_G] = pointer[PIXELFORMAT_INDEX_G + offset];
color[COLOR_INDEX_B] = pointer[PIXELFORMAT_INDEX_B + offset];
color[COLOR_INDEX_A] = pointer[PIXELFORMAT_INDEX_A + offset];
}
///
/// Set the color at the specified location from the specified array
///
///
///
/// byte[4] as reference (r,g,b,a)
public override void SetColorAt(int x, int y, byte[] color)
{
int offset = (x * 4) + (y * stride);
pointer[PIXELFORMAT_INDEX_R + offset] = color[COLOR_INDEX_R]; // R
pointer[PIXELFORMAT_INDEX_G + offset] = color[COLOR_INDEX_G];
pointer[PIXELFORMAT_INDEX_B + offset] = color[COLOR_INDEX_B];
pointer[PIXELFORMAT_INDEX_A + offset] = color[COLOR_INDEX_A];
}
///
/// Retrieve the color, without alpha (is blended), at location x,y
/// Before the first time this is called the Lock() should be called once!
///
/// X coordinate
/// Y Coordinate
/// Color
public Color GetBlendedColorAt(int x, int y)
{
int offset = (x * 4) + (y * stride);
int a = pointer[PIXELFORMAT_INDEX_A + offset];
int red = pointer[PIXELFORMAT_INDEX_R + offset];
int green = pointer[PIXELFORMAT_INDEX_G + offset];
int blue = pointer[PIXELFORMAT_INDEX_B + offset];
if (a < 255)
{
// As the request is to get without alpha, we blend.
int rem = 255 - a;
red = (red * a + BackgroundBlendColor.R * rem) / 255;
green = (green * a + BackgroundBlendColor.G * rem) / 255;
blue = (blue * a + BackgroundBlendColor.B * rem) / 255;
}
return Color.FromArgb(255, red, green, blue);
}
}
}