Saving Transparent PNG as Transparent GIF - c#

I'm trying to Resizing a Transparent png and saving it as a single frame gif image.
Let skip resizing part, while you try to save a Transparent png as gif, you will see a black background in output gif:
Bitmap n = new Bitmap(targetPngPath);
n.Save(#"C:\1.gif", ImageFormat.Gif);
yes, I can make that black background into white, but it is not what I'm looking for. Even I can remove the black color using MakeTransparent Method, But it will remove about every black color in image and we will not have a standard transparent image.
we also can do a TRICK saving gif image, we keep extension in filename but we will save it as PNG Format like this:
n.Save(#"C:\1.gif", ImageFormat.Png);
But it is not also standard.
So is there any way to safely save a transparent png as a gif image with transparency?
PNG =
GIF =
GIF Saved With Photoshop =

This is because the built-in GIF encoder cannot handle the source well, unless it is already a 8 bpp image. You must convert your PNG image to a 256 color image first, then you can save it correctly with the GIF encoder.
public static void SaveGif(string fileName, Image image)
{
int bpp = Image.GetPixelFormatSize(image.PixelFormat);
if (bpp == 8)
{
image.Save(fileName, ImageFormat.Gif);
return;
}
// 1 and 4 bpp images are need to be converted, too; otherwise, gif encoder encodes the image from 32 bpp image resulting 256 color, no transparency
if (bpp < 8)
{
using (Image image8Bpp = ConvertPixelFormat(image, PixelFormat.Format8bppIndexed, null))
{
image8Bpp.Save(fileName, ImageFormat.Gif);
return;
}
}
// high/true color bitmap: obtaining the colors
// Converting always to 8 bpp pixel format; otherwise, gif encoder would convert it to 32 bpp first.
// With 8 bpp, gif encoder will preserve transparency and will save compact palette
// Note: This works well for 256 color images in a 32bpp bitmap. Otherwise, you might try to pass null as palette so a default palette will be used.
Color[] palette = GetColors((Bitmap)image, 256);
using (Image imageIndexed = ConvertPixelFormat(image, PixelFormat.Format8bppIndexed, palette))
{
imageIndexed.Save(fileName, ImageFormat.Gif);
}
}
// TODO: Use some quantizer
private static Color[] GetColors(Bitmap bitmap, int maxColors)
{
if (bitmap == null)
throw new ArgumentNullException("bitmap");
if (maxColors < 0)
throw new ArgumentOutOfRangeException("maxColors");
HashSet<int> colors = new HashSet<int>();
PixelFormat pixelFormat = bitmap.PixelFormat;
if (Image.GetPixelFormatSize(pixelFormat) <= 8)
return bitmap.Palette.Entries;
// 32 bpp source: the performant variant
if (pixelFormat == PixelFormat.Format32bppRgb ||
pixelFormat == PixelFormat.Format32bppArgb ||
pixelFormat == PixelFormat.Format32bppPArgb)
{
BitmapData data = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size), ImageLockMode.ReadOnly, pixelFormat);
try
{
unsafe
{
byte* line = (byte*)data.Scan0;
for (int y = 0; y < data.Height; y++)
{
for (int x = 0; x < data.Width; x++)
{
int c = ((int*)line)[x];
// if alpha is 0, adding the transparent color
if ((c >> 24) == 0)
c = 0xFFFFFF;
if (colors.Contains(c))
continue;
colors.Add(c);
if (colors.Count == maxColors)
return colors.Select(Color.FromArgb).ToArray();
}
line += data.Stride;
}
}
}
finally
{
bitmap.UnlockBits(data);
}
}
else
{
// fallback: getpixel
for (int y = 0; y < bitmap.Height; y++)
{
for (int x = 0; x < bitmap.Width; x++)
{
int c = bitmap.GetPixel(x, y).ToArgb();
if (colors.Contains(c))
continue;
colors.Add(c);
if (colors.Count == maxColors)
return colors.Select(Color.FromArgb).ToArray();
}
}
}
return colors.Select(Color.FromArgb).ToArray();
}
private static Image ConvertPixelFormat(Image image, PixelFormat newPixelFormat, Color[] palette)
{
if (image == null)
throw new ArgumentNullException("image");
PixelFormat sourcePixelFormat = image.PixelFormat;
int bpp = Image.GetPixelFormatSize(newPixelFormat);
if (newPixelFormat == PixelFormat.Format16bppArgb1555 || newPixelFormat == PixelFormat.Format16bppGrayScale)
throw new NotSupportedException("This pixel format is not supported by GDI+");
Bitmap result;
// non-indexed target image (transparency preserved automatically)
if (bpp > 8)
{
result = new Bitmap(image.Width, image.Height, newPixelFormat);
using (Graphics g = Graphics.FromImage(result))
{
g.DrawImage(image, 0, 0, image.Width, image.Height);
}
return result;
}
int transparentIndex;
Bitmap bmp;
// indexed colors: using GDI+ natively
RGBQUAD[] targetPalette = new RGBQUAD[256];
int colorCount = InitPalette(targetPalette, bpp, (image is Bitmap) ? image.Palette : null, palette, out transparentIndex);
BITMAPINFO bmi = new BITMAPINFO();
bmi.icHeader.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER));
bmi.icHeader.biWidth = image.Width;
bmi.icHeader.biHeight = image.Height;
bmi.icHeader.biPlanes = 1;
bmi.icHeader.biBitCount = (ushort)bpp;
bmi.icHeader.biCompression = BI_RGB;
bmi.icHeader.biSizeImage = (uint)(((image.Width + 7) & 0xFFFFFFF8) * image.Height / (8 / bpp));
bmi.icHeader.biXPelsPerMeter = 0;
bmi.icHeader.biYPelsPerMeter = 0;
bmi.icHeader.biClrUsed = (uint)colorCount;
bmi.icHeader.biClrImportant = (uint)colorCount;
bmi.icColors = targetPalette;
bmp = (image as Bitmap) ?? new Bitmap(image);
// Creating the indexed bitmap
IntPtr bits;
IntPtr hbmResult = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits, IntPtr.Zero, 0);
// Obtaining screen DC
IntPtr dcScreen = GetDC(IntPtr.Zero);
// DC for the original hbitmap
IntPtr hbmSource = bmp.GetHbitmap();
IntPtr dcSource = CreateCompatibleDC(dcScreen);
SelectObject(dcSource, hbmSource);
// DC for the indexed hbitmap
IntPtr dcTarget = CreateCompatibleDC(dcScreen);
SelectObject(dcTarget, hbmResult);
// Copy content
BitBlt(dcTarget, 0, 0, image.Width, image.Height, dcSource, 0, 0, 0x00CC0020 /*TernaryRasterOperations.SRCCOPY*/);
// obtaining result
result = Image.FromHbitmap(hbmResult);
result.SetResolution(image.HorizontalResolution, image.VerticalResolution);
// cleanup
DeleteDC(dcSource);
DeleteDC(dcTarget);
ReleaseDC(IntPtr.Zero, dcScreen);
DeleteObject(hbmSource);
DeleteObject(hbmResult);
ColorPalette resultPalette = result.Palette;
bool resetPalette = false;
// restoring transparency
if (transparentIndex >= 0)
{
// updating palette if transparent color is not actually transparent
if (resultPalette.Entries[transparentIndex].A != 0)
{
resultPalette.Entries[transparentIndex] = Color.Transparent;
resetPalette = true;
}
ToIndexedTransparentByArgb(result, bmp, transparentIndex);
}
if (resetPalette)
result.Palette = resultPalette;
if (!ReferenceEquals(bmp, image))
bmp.Dispose();
return result;
}
private static int InitPalette(RGBQUAD[] targetPalette, int bpp, ColorPalette originalPalette, Color[] desiredPalette, out int transparentIndex)
{
int maxColors = 1 << bpp;
// using desired palette
Color[] sourcePalette = desiredPalette;
// or, using original palette if it has fewer or the same amount of colors as requested
if (sourcePalette == null && originalPalette != null && originalPalette.Entries.Length > 0 && originalPalette.Entries.Length <= maxColors)
sourcePalette = originalPalette.Entries;
// or, using default system palette
if (sourcePalette == null)
{
using (Bitmap bmpReference = new Bitmap(1, 1, GetPixelFormat(bpp)))
{
sourcePalette = bmpReference.Palette.Entries;
}
}
// it is ignored if source has too few colors (rest of the entries will be black)
transparentIndex = -1;
bool hasBlack = false;
int colorCount = Math.Min(maxColors, sourcePalette.Length);
for (int i = 0; i < colorCount; i++)
{
targetPalette[i] = new RGBQUAD(sourcePalette[i]);
if (transparentIndex == -1 && sourcePalette[i].A == 0)
transparentIndex = i;
if (!hasBlack && (sourcePalette[i].ToArgb() & 0xFFFFFF) == 0)
hasBlack = true;
}
// if transparent index is 0, relocating it and setting transparent index to 1
if (transparentIndex == 0)
{
targetPalette[0] = targetPalette[1];
transparentIndex = 1;
}
// otherwise, setting the color of transparent index the same as the previous color, so it will not be used during the conversion
else if (transparentIndex != -1)
{
targetPalette[transparentIndex] = targetPalette[transparentIndex - 1];
}
// if black color is not found in palette, counting 1 extra colors because it can be used in conversion
if (colorCount < maxColors && !hasBlack)
colorCount++;
return colorCount;
}
private unsafe static void ToIndexedTransparentByArgb(Bitmap target, Bitmap source, int transparentIndex)
{
int sourceBpp = Image.GetPixelFormatSize(source.PixelFormat);
int targetBpp = Image.GetPixelFormatSize(target.PixelFormat);
BitmapData dataTarget = target.LockBits(new Rectangle(Point.Empty, target.Size), ImageLockMode.ReadWrite, target.PixelFormat);
BitmapData dataSource = source.LockBits(new Rectangle(Point.Empty, source.Size), ImageLockMode.ReadOnly, source.PixelFormat);
try
{
byte* lineSource = (byte*)dataSource.Scan0;
byte* lineTarget = (byte*)dataTarget.Scan0;
bool is32Bpp = sourceBpp == 32;
// scanning through the lines
for (int y = 0; y < dataSource.Height; y++)
{
// scanning through the pixels within the line
for (int x = 0; x < dataSource.Width; x++)
{
// testing if pixel is transparent (applies both argb and pargb)
if (is32Bpp && ((uint*)lineSource)[x] >> 24 == 0
|| !is32Bpp && ((ulong*)lineSource)[x] >> 48 == 0UL)
{
switch (targetBpp)
{
case 8:
lineTarget[x] = (byte)transparentIndex;
break;
case 4:
// First pixel is the high nibble
int pos = x >> 1;
byte nibbles = lineTarget[pos];
if ((x & 1) == 0)
{
nibbles &= 0x0F;
nibbles |= (byte)(transparentIndex << 4);
}
else
{
nibbles &= 0xF0;
nibbles |= (byte)transparentIndex;
}
lineTarget[pos] = nibbles;
break;
case 1:
// First pixel is MSB.
pos = x >> 3;
byte mask = (byte)(128 >> (x & 7));
if (transparentIndex == 0)
lineTarget[pos] &= (byte)~mask;
else
lineTarget[pos] |= mask;
break;
}
}
}
lineSource += dataSource.Stride;
lineTarget += dataTarget.Stride;
}
}
finally
{
target.UnlockBits(dataTarget);
source.UnlockBits(dataSource);
}
}
private static PixelFormat GetPixelFormat(int bpp)
{
switch (bpp)
{
case 1:
return PixelFormat.Format1bppIndexed;
case 4:
return PixelFormat.Format4bppIndexed;
case 8:
return PixelFormat.Format8bppIndexed;
case 16:
return PixelFormat.Format16bppRgb565;
case 24:
return PixelFormat.Format24bppRgb;
case 32:
return PixelFormat.Format32bppArgb;
case 48:
return PixelFormat.Format48bppRgb;
case 64:
return PixelFormat.Format64bppArgb;
default:
throw new ArgumentOutOfRangeException("bpp");
}
}
And the native types and methods:
private const int BI_RGB = 0;
private const int DIB_RGB_COLORS = 0;
[DllImport("gdi32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BITMAPINFO pbmi, int iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, uint dwRop);
[DllImport("gdi32.dll")]
private static extern bool DeleteDC(IntPtr hdc);
[DllImport("gdi32.dll", SetLastError = true)]
private static extern bool DeleteObject(IntPtr hObject);
[DllImport("user32.dll")]
private static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);
[StructLayout(LayoutKind.Sequential)]
private struct RGBQUAD
{
internal byte rgbBlue;
internal byte rgbGreen;
internal byte rgbRed;
internal byte rgbReserved;
internal RGBQUAD(Color color)
{
rgbRed = color.R;
rgbGreen = color.G;
rgbBlue = color.B;
rgbReserved = 0;
}
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFO
{
public BITMAPINFOHEADER icHeader;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)]
public RGBQUAD[] icColors;
}
[StructLayout(LayoutKind.Sequential)]
private struct BITMAPINFOHEADER
{
internal uint biSize;
internal int biWidth;
internal int biHeight;
internal ushort biPlanes;
internal ushort biBitCount;
internal uint biCompression;
internal uint biSizeImage;
internal int biXPelsPerMeter;
internal int biYPelsPerMeter;
internal uint biClrUsed;
internal uint biClrImportant;
}
Update:
My Drawing Libraries are now free to download. It makes a SaveAsGif extension method available on the Image type:
using KGySoft.Drawing;
/// ...
using (var stream = new FileStream(targetPngPath, FileMode.Create))
{
// You can either use an arbitrary palette,
myPngBitmap.SaveAsGif(stream, myPngBitmap.GetColors(256));
// or, you can let the built-in encoder use dithering with a fixed palette.
// Pixel format is adjusted so transparency will be preserved.
myPngBitmap.SaveAsGif(stream, allowDithering: true);
}

This may help.
The Bitmap class does not save correctly with transparency.
You need to cast Bitmap to Image.
c# Bitmap.Save transparancy doesn't save in png
There are comments on the internet about .NET not saving Bitmaps with Transparency correctly .
Here is a good link for further reading, too much code to post.
http://forums.asp.net/t/1057792.aspx?ASP+NET+C+Making+an+Image+transparent

Related

CopyFromScreen and GetPixel performance issues [duplicate]

Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.
This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.
Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.
This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}
Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.
As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}
This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);

Convert Specific BitmapImage Colour to Transparent

I'm currently recreating my Image Explorer application, formerly written in Windows Forms to the Windows Presentation Framework.
My WinForms application was using the WindowsThumbnailProvider from #DanielPeñalba (See this link for the original version of the code)
WinForms Version - Successfully converting 0 alpha, 0 red, 0 green and 0 blue to Transparent
WPF Version - Almost working
WPF Code - Slightly modified version of the original WindowsThumbnailProvider to support System.Windows.Media.Imaging.BitmapImage instead of System.Drawing.Bitmap
MainWindow.xaml - For all the testing
<Window x:Class="WpfFileFolderThumbnails.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:WpfFileFolderThumbnails"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="600">
<Grid>
<Image x:Name="ThumbnailImage1" HorizontalAlignment="Left" Height="256" Margin="10,20,0,0" VerticalAlignment="Top" Width="256"/>
<Image x:Name="ThumbnailImage2" HorizontalAlignment="Left" Height="256" Margin="326,20,0,0" VerticalAlignment="Top" Width="256"/>
</Grid>
</Window>
MainWindow.xaml.cs - Test code to call the GetThumbnail and CreateAlphaBitmapImage methods
using System.Windows;
namespace WpfFileFolderThumbnails
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
this.InitializeComponent();
var thumbnail = WindowsThumbnailProviderWpf.GetThumbnail(#"D:\Pictures\Art\Anime", 256, 256,
ThumbnailOptions.ThumbnailOnly);
var alphaThumbnail = WindowsThumbnailProviderWpf.CreateAlphaBitmapImage(thumbnail);
this.ThumbnailImage1.Source = thumbnail;
this.ThumbnailImage2.Source = alphaThumbnail;
}
}
}
WindowsThumbnailProviderWpf.cs - Class to get Folder Thumbnail and make it Transparent
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Path = System.IO.Path;
namespace WpfFileFolderThumbnails
{
[Flags]
public enum ThumbnailOptions
{
None = 0x00,
BiggerSizeOk = 0x01,
InMemoryOnly = 0x02,
IconOnly = 0x04,
ThumbnailOnly = 0x08,
InCacheOnly = 0x10,
}
public static class WindowsThumbnailProviderWpf
{
private const string IShellItem2Guid = "7E9FB0D3-919F-4307-AB2E-9B1860310C93";
[DllImport("shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern int SHCreateItemFromParsingName(
[MarshalAs(UnmanagedType.LPWStr)] string path,
// The following parameter is not used - binding context.
IntPtr pbc,
ref Guid riid,
[MarshalAs(UnmanagedType.Interface)] out IShellItem shellItem);
[DllImport("gdi32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool DeleteObject(IntPtr hObject);
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("43826d1e-e718-42ee-bc55-a1e261c37bfe")]
internal interface IShellItem
{
void BindToHandler(IntPtr pbc,
[MarshalAs(UnmanagedType.LPStruct)]Guid bhid,
[MarshalAs(UnmanagedType.LPStruct)]Guid riid,
out IntPtr ppv);
void GetParent(out IShellItem ppsi);
void GetDisplayName(SIGDN sigdnName, out IntPtr ppszName);
void GetAttributes(uint sfgaoMask, out uint psfgaoAttribs);
void Compare(IShellItem psi, uint hint, out int piOrder);
};
internal enum SIGDN : uint
{
NORMALDISPLAY = 0,
PARENTRELATIVEPARSING = 0x80018001,
PARENTRELATIVEFORADDRESSBAR = 0x8001c001,
DESKTOPABSOLUTEPARSING = 0x80028000,
PARENTRELATIVEEDITING = 0x80031001,
DESKTOPABSOLUTEEDITING = 0x8004c000,
FILESYSPATH = 0x80058000,
URL = 0x80068000
}
internal enum HResult
{
Ok = 0x0000,
False = 0x0001,
InvalidArguments = unchecked((int)0x80070057),
OutOfMemory = unchecked((int)0x8007000E),
NoInterface = unchecked((int)0x80004002),
Fail = unchecked((int)0x80004005),
ElementNotFound = unchecked((int)0x80070490),
TypeElementNotFound = unchecked((int)0x8002802B),
NoObject = unchecked((int)0x800401E5),
Win32ErrorCanceled = 1223,
Canceled = unchecked((int)0x800704C7),
ResourceInUse = unchecked((int)0x800700AA),
AccessDenied = unchecked((int)0x80030005)
}
[ComImport()]
[Guid("bcc18b79-ba16-442f-80c4-8a59c30c463b")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IShellItemImageFactory
{
[PreserveSig]
HResult GetImage(
[In, MarshalAs(UnmanagedType.Struct)] NativeSize size,
[In] ThumbnailOptions flags,
[Out] out IntPtr phbm);
}
[StructLayout(LayoutKind.Sequential)]
internal struct NativeSize
{
private int width;
private int height;
public int Width { set { this.width = value; } }
public int Height { set { this.height = value; } }
};
[StructLayout(LayoutKind.Sequential)]
public struct RGBQUAD
{
public byte rgbBlue;
public byte rgbGreen;
public byte rgbRed;
public byte rgbReserved;
}
public static BitmapImage GetThumbnail(string fileName, int width, int height, ThumbnailOptions options)
{
IntPtr hBitmap = GetHBitmap(Path.GetFullPath(fileName), width, height, options);
try
{
// return a System.Drawing.Bitmap from the hBitmap
return GetBitmapImageFromHBitmap(hBitmap);
}
finally
{
// delete HBitmap to avoid memory leaks
DeleteObject(hBitmap);
}
}
public static BitmapImage GetBitmapImageFromHBitmap(IntPtr nativeHBitmap)
{
var bmpSource = Imaging.CreateBitmapSourceFromHBitmap(nativeHBitmap,
IntPtr.Zero,
Int32Rect.Empty,
BitmapSizeOptions.FromEmptyOptions());
var bmpImage = BitmapSourceToBitmapImage(bmpSource);
return bmpImage;
}
// Conversion code
public static BitmapImage BitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
JpegBitmapEncoder encoder = new JpegBitmapEncoder();
MemoryStream memorystream = new MemoryStream();
BitmapImage tmpImage = new BitmapImage();
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
encoder.Save(memorystream);
tmpImage.BeginInit();
tmpImage.StreamSource = new MemoryStream(memorystream.ToArray());
tmpImage.EndInit();
memorystream.Close();
return tmpImage;
}
public static BitmapImage CreateAlphaBitmapImage(BitmapImage sourceBitmapImage)
{
var bmp = sourceBitmapImage.Clone();
var pixels = new int[(int)bmp.Width * (int)bmp.Height];
var stride = (bmp.PixelWidth * bmp.Format.BitsPerPixel + 7) / 8;
bmp.CopyPixels(pixels, stride, 0);
var oldColor = pixels[0];
var red = 255;
var green = 255;
var blue = 255;
var alpha = 0;
var color = (alpha << 24) + (red << 16) + (green << 8) + blue;
for (var i = 0; i < (int)bmp.Width * (int)bmp.Height; i++)
{
if (pixels[i] == oldColor)
{
pixels[i] = color;
}
}
//remake the bitmap source with these pixels
var source = BitmapSource.Create(bmp.PixelWidth, bmp.PixelHeight, bmp.DpiX, bmp.DpiY, PixelFormats.Bgra32, bmp.Palette, pixels, stride);
//return sourceBitmapImage;
return BitmapSourceToBitmapImage(source);
}
private static IntPtr GetHBitmap(string fileName, int width, int height, ThumbnailOptions options)
{
IShellItem nativeShellItem;
Guid shellItem2Guid = new Guid(IShellItem2Guid);
int retCode = SHCreateItemFromParsingName(fileName, IntPtr.Zero, ref shellItem2Guid, out nativeShellItem);
if (retCode != 0)
throw Marshal.GetExceptionForHR(retCode);
NativeSize nativeSize = new NativeSize();
nativeSize.Width = width;
nativeSize.Height = height;
IntPtr hBitmap;
HResult hr = ((IShellItemImageFactory)nativeShellItem).GetImage(nativeSize, options, out hBitmap);
Marshal.ReleaseComObject(nativeShellItem);
if (hr == HResult.Ok) return hBitmap;
throw Marshal.GetExceptionForHR((int)hr);
}
}
}
While i understand i could just simply reference System.Drawing and use the already working solution, i'd like to know if it's possible to do the same thing in WPF.
Question - Is there a simple way to loop through each pixel of a BitmapImage (similar to a Bitmap), change a specific pixel combination and create a copy of the BitmapImage with transparency?
You may call CopyPixels on a BitmapSource to get the raw pixel buffer, then modify the buffer as you like, and create a new BitmapSource from the modified buffer.
The method below shows how this could work for a BitmapSource with a 32-bit BGRA format.
private static BitmapSource CreateTransparency(BitmapSource source)
{
if (source.Format != PixelFormats.Bgra32)
{
return source;
}
var bytesPerPixel = (source.Format.BitsPerPixel + 7) / 8;
var stride = bytesPerPixel * source.PixelWidth;
var buffer = new byte[stride * source.PixelHeight];
source.CopyPixels(buffer, stride, 0);
for (int y = 0; y < source.PixelHeight; y++)
{
for (int x = 0; x < source.PixelWidth; x++)
{
var i = stride * y + bytesPerPixel * x;
var b = buffer[i];
var g = buffer[i + 1];
var r = buffer[i + 2];
var a = buffer[i + 3];
if (...)
{
buffer[i + 3] = 0d; // set transparent
}
}
}
return BitmapSource.Create(
source.PixelWidth, source.PixelHeight,
source.DpiX, source.DpiY,
source.Format, null, buffer, stride);
}

Getting transparent shell-icons for files and folders in C#

I'm currently working on a small library that enables you to get icons from files and folders. Now, I don't care if it only works on win8+ (cause that's the place I'm going to use it), however, I've run in to a tiny problem with regards to transparency. If you take a look at the following image:
The one I generate (from my library) is to the left, windows explorer is to the right.
Now, as you might see, first off there is 2 black lines in the upper right of the one I generate, second, there is a difference in the background color. So what I'm wondering is this; is there no way to get the exact same image used by windows explorer, or am I simply doing it wrong?
My code (with exception to structs/externs etc. for shortness) bellow, entire code here.
public static class Icon
{
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// zero out the RGB values for all pixels with A == 0
// (AlphaBlend expects them to all be zero)
for (int i = 0; i < dibsection.dsBmih.biWidth * dibsection.dsBmih.biHeight; i++)
{
IntPtr ptr = dibsection.dsBm.bmBits + (i * Marshal.SizeOf(typeof(RGBQUAD)));
var rgbquad = (RGBQUAD)Marshal.PtrToStructure(ptr, typeof(RGBQUAD));
if (rgbquad.rgbReserved == 0)
{
rgbquad.rgbBlue = 0;
rgbquad.rgbGreen = 0;
rgbquad.rgbRed = 0;
}
else
{
;
}
Marshal.StructureToPtr(rgbquad, ptr, false);
}
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
// get the HDCs and select the HBITMAP
Graphics graphics = Graphics.FromImage(bitmap);
IntPtr hdcDest = graphics.GetHdc();
IntPtr hdcSrc = Gdi32.CreateCompatibleDC(hdcDest);
IntPtr hobjOriginal = Gdi32.SelectObject(hdcSrc, hbitmap);
// render the bitmap using AlphaBlend
BLENDFUNCTION blendfunction = new BLENDFUNCTION(BLENDFUNCTION.AC_SRC_OVER, 0, 0xFF, BLENDFUNCTION.AC_SRC_ALPHA);
Gdi32.AlphaBlend(hdcDest, 0, 0, width, height, hdcSrc, 0, 0, width, height, blendfunction);
// clean up
Gdi32.SelectObject(hdcSrc, hobjOriginal);
Gdi32.DeleteDC(hdcSrc);
graphics.ReleaseHdc(hdcDest);
graphics.Dispose();
Gdi32.DeleteObject(hbitmap);
return bitmap;
}
}
It seems copying pixel by pixel was the solution. The following seems to be pixel-perfect equal to the explorer one.
public static Image GetIcon(string fileName, int size)
{
IShellItem shellItem;
Shell32.SHCreateItemFromParsingName(fileName, IntPtr.Zero, Shell32.IShellItem_GUID, out shellItem);
IntPtr hbitmap;
((IShellItemImageFactory)shellItem).GetImage(new SIZE(size, size), 0x0, out hbitmap);
// get the info about the HBITMAP inside the IPictureDisp
DIBSECTION dibsection = new DIBSECTION();
Gdi32.GetObjectDIBSection(hbitmap, Marshal.SizeOf(dibsection), ref dibsection);
int width = dibsection.dsBm.bmWidth;
int height = dibsection.dsBm.bmHeight;
// create the destination Bitmap object
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
for (int x = 0; x < dibsection.dsBmih.biWidth; x++)
{
for (int y = 0; y < dibsection.dsBmih.biHeight; y++)
{
int i = y * dibsection.dsBmih.biWidth + x;
IntPtr ptr = dibsection.dsBm.bmBits + (i * Marshal.SizeOf(typeof(RGBQUAD)));
var rgbquad = (RGBQUAD)Marshal.PtrToStructure(ptr, typeof(RGBQUAD));
if (rgbquad.rgbReserved != 0)
bitmap.SetPixel(x, y, Color.FromArgb(rgbquad.rgbReserved, rgbquad.rgbRed, rgbquad.rgbGreen, rgbquad.rgbBlue));
}
}
Gdi32.DeleteObject(hbitmap);
return bitmap;
}

Get image as Image Class with twain in c#

I can connect and get images from my device with twaindotnet. But I want to handle the images as Image class. When I try something like this:
...
ArrayList pics = tw.TransferPictures();
EndingScan();
tw.CloseSrc();
if(pics.Count > 0) {
IntPtr img = (IntPtr) pics[ 0 ];
PicForm newpic = new PicForm( img );
Image r = Image.FromHbitmap(img, this.Handle);
picturebox.Image = r;
}
...
I'm getting an error as "Error:Generic Error Occured in GDI+" on the line ,
Image r = Image.FromHbitmap(img, this.Handle);
So where am I wrong? How can I get as an Image the image?
I too found out that calling Image.FromHbitmap is not enough.
I had a look in the TwainDotNet library you mentioned and there I found the BitmapRenderer class.
Pulling out just the relevant bit it is easy enough to use that to create a simple static method that you can pass in the IntPtr you get from TWAIN (in your case your img variable) and convert it to a Bitmap. You, therefore, call it like so:
Image r = TwainBitmapConvertor.ToBitmap(img);
and here is the code (it only works on x86 and needs tidying but it does the job):
public static class TwainBitmapConvertor
{
[StructLayout(LayoutKind.Sequential, Pack = 2)]
private class BitmapInfoHeader
{
public int Size;
public int Width;
public int Height;
public short Planes;
public short BitCount;
public int Compression;
public int SizeImage;
public int XPelsPerMeter;
public int YPelsPerMeter;
public int ClrUsed;
public int ClrImportant;
}
[DllImport("gdi32.dll", ExactSpelling = true)]
private static extern int SetDIBitsToDevice(IntPtr hdc,
int xdst, int ydst, int width, int height, int xsrc,
int ysrc, int start, int lines, IntPtr bitsptr,
IntPtr bmiptr, int color);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalLock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern bool GlobalUnlock(IntPtr handle);
[DllImport("kernel32.dll", ExactSpelling = true)]
private static extern IntPtr GlobalFree(IntPtr handle);
public static Bitmap ToBitmap(IntPtr dibHandle)
{
var bitmapPointer = GlobalLock(dibHandle);
var bitmapInfo = new BitmapInfoHeader();
Marshal.PtrToStructure(bitmapPointer, bitmapInfo);
var rectangle = new Rectangle();
rectangle.X = rectangle.Y = 0;
rectangle.Width = bitmapInfo.Width;
rectangle.Height = bitmapInfo.Height;
if (bitmapInfo.SizeImage == 0)
{
bitmapInfo.SizeImage =
((((bitmapInfo.Width * bitmapInfo.BitCount) + 31) & ~31) >> 3)
* bitmapInfo.Height;
}
// The following code only works on x86
Debug.Assert(Marshal.SizeOf(typeof(IntPtr)) == 4);
int pixelInfoPointer = bitmapInfo.ClrUsed;
if ((pixelInfoPointer == 0) && (bitmapInfo.BitCount <= 8))
{
pixelInfoPointer = 1 << bitmapInfo.BitCount;
}
pixelInfoPointer = (pixelInfoPointer * 4) + bitmapInfo.Size
+ bitmapPointer.ToInt32();
IntPtr pixelInfoIntPointer = new IntPtr(pixelInfoPointer);
Bitmap bitmap = new Bitmap(rectangle.Width, rectangle.Height);
using (Graphics graphics = Graphics.FromImage(bitmap))
{
IntPtr hdc = graphics.GetHdc();
try
{
SetDIBitsToDevice(hdc,
0, 0, rectangle.Width, rectangle.Height, 0, 0, 0,
rectangle.Height, pixelInfoIntPointer, bitmapPointer, 0);
}
finally
{
graphics.ReleaseHdc(hdc);
}
}
bitmap.SetResolution(PpmToDpi(bitmapInfo.XPelsPerMeter),
PpmToDpi(bitmapInfo.YPelsPerMeter));
GlobalUnlock(dibHandle);
GlobalFree(dibHandle);
return bitmap;
}
private static float PpmToDpi(double pixelsPerMeter)
{
double pixelsPerMillimeter = (double)pixelsPerMeter / 1000.0;
double dotsPerInch = pixelsPerMillimeter * 25.4;
return (float)Math.Round(dotsPerInch, 2);
}
}
Assuming that tw.TransferPictures() returns an array of bitmap handles, then change Image r = ... to:
Image r = Image.FromHbitmap(img);
The second argument to FromHbitmap is a handle to a GDI palette, which I doubt you have.

How to read the Color of a Screen Pixel

Okay, I am looking for a function or something that will read the color of a certain pixel on my monitor, and when that color is detected, another function will be enabled. I figure using RGB. All help appreciated. Thank You.
This is the most efficient: It grabs a pixel at the location of the cursor, and doesn't rely on only having one monitor.
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using System.Diagnostics;
namespace FormTest
{
public partial class Form1 : Form
{
[DllImport("user32.dll")]
static extern bool GetCursorPos(ref Point lpPoint);
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
public Form1()
{
InitializeComponent();
}
private void MouseMoveTimer_Tick(object sender, EventArgs e)
{
Point cursor = new Point();
GetCursorPos(ref cursor);
var c = GetColorAt(cursor);
this.BackColor = c;
if (c.R == c.G && c.G < 64 && c.B > 128)
{
MessageBox.Show("Blue");
}
}
Bitmap screenPixel = new Bitmap(1, 1, PixelFormat.Format32bppArgb);
public Color GetColorAt(Point location)
{
using (Graphics gdest = Graphics.FromImage(screenPixel))
{
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
}
return screenPixel.GetPixel(0, 0);
}
}
}
Now, obviously, you don't have to use the cursor's current location, but this is the general idea.
EDIT:
Given the above GetColorAt function you can poll a certain pixel on the screen in a safe, performance friendly way like this:
private void PollPixel(Point location, Color color)
{
while(true)
{
var c = GetColorAt(location);
if (c.R == color.R && c.G == color.G && c.B == color.B)
{
DoAction();
return;
}
// By calling Thread.Sleep() without a parameter, we are signaling to the
// operating system that we only want to sleep long enough for other
// applications. As soon as the other apps yield their CPU time, we will
// regain control.
Thread.Sleep()
}
}
You can wrap that in a Thread if you want, or execute it from a Console application. "Whatever suits your fancy," I guess.
Most answers here use the very same source of that pixel (desktop dc).
The key function is GetPixel.
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetDesktopWindow();
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr window);
[DllImport("gdi32.dll", SetLastError = true)]
public static extern uint GetPixel(IntPtr dc, int x, int y);
[DllImport("user32.dll", SetLastError = true)]
public static extern int ReleaseDC(IntPtr window, IntPtr dc);
public static Color GetColorAt(int x, int y)
{
IntPtr desk = GetDesktopWindow();
IntPtr dc = GetWindowDC(desk);
int a = (int) GetPixel(dc, x, y);
ReleaseDC(desk, dc);
return Color.FromArgb(255, (a >> 0) & 0xff, (a >> 8) & 0xff, (a >> 16) & 0xff);
}
I think this is the cleanest and quickest way.
Note:
If you have modified the default text size among the Display Settings on Windows to increase readability on a high resolution display, the coordinate parameters of GetPixel() need to be adjusted the same way. For example, if the cursor location is (x,y) with 150% of text size on Windows 7, you need to call GetPixel(x*1.5, y*1.5) to get the color of the pixel under the cursor.
This function is shorter and can achieve the same result using System.Drawing, without Pinvoke.
Color GetColorAt(int x, int y)
{
Bitmap bmp = new Bitmap(1, 1);
Rectangle bounds = new Rectangle(x, y, 1, 1);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(bounds.Location, Point.Empty, bounds.Size);
return bmp.GetPixel(0, 0);
}
Please check this two different functions I have used in one of my previous projects :
1) This function takes snapshot of Desktop
private void CaptureScreenAndSave(string strSavePath)
{
//SetTitle("Capturing Screen...");
Bitmap bmpScreenshot;
Graphics gfxScreenshot;
bmpScreenshot = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height,System.Drawing.Imaging.PixelFormat.Format32bppArgb);
gfxScreenshot = Graphics.FromImage(bmpScreenshot);
gfxScreenshot.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
MemoryStream msIn = new MemoryStream();
bmpScreenshot.Save(msIn, System.Drawing.Imaging.ImageCodecInfo.GetImageEncoders()[0], null);
msIn.Close();
byte[] buf = msIn.ToArray();
MemoryStream msOut = new MemoryStream();
msOut.Write(buf, 0, buf.Length);
msOut.Position = 0;
Bitmap bmpOut = new Bitmap(msOut);
try
{
bmpOut.Save(strSavePath, System.Drawing.Imaging.ImageFormat.Bmp);
//SetTitle("Capturing Screen Image Saved...");
}
catch (Exception exp)
{
}
finally
{
msOut.Close();
}
}
2) This function takes an image in input and calculates RGB average of pixel range given.
double GetRGBAverageForPixelRange( int istartRange, int iEndRange, Bitmap oBitmap )
{
double dRetnVal = 0 ;
Color oTempColor ;
int i, j ;
for( int iCounter = istartRange ; iCounter < iEndRange ; iCounter++ )
{
i = (iCounter % (oBitmap.Width));
j = ( iCounter / ( oBitmap.Width ) ) ;
if (i >= 0 && j >= 0 && i < oBitmap.Width && j < oBitmap.Height )
{
oTempColor = oBitmap.GetPixel(i, j);
dRetnVal = dRetnVal + oTempColor.ToArgb();
}
}
return dRetnVal ;
}
This two functions together might solve your problem. Happy Coding :)
EDIT : Please note that GetPixel is very slow function. I will think twice befor using it.
As far as I know the easiest way to do this is to:
take a screenshot
look at the bitmap and get the pixel color
Edit
There is probably no way to "wait" until the pixel changes to a certain color. Your program will probably have to just loop and check it every so often until it sees the color.
For example:
while(!IsPixelColor(x, y, color))
{
//probably best to add a sleep here so your program doesn't use too much CPU
}
DoAction();
EDIT 2
Here is some sample code you can modify. This code just changes the color of a label based on the current color in a given pixel. This code avoids the handle leak mentioned.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Runtime.InteropServices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
[DllImport("gdi32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
public static extern int BitBlt(IntPtr hDC, int x, int y, int nWidth, int nHeight, IntPtr hSrcDC, int xSrc, int ySrc, int dwRop);
Thread t;
int x, y;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
x = 20;
y = 50;
t = new Thread(update);
t.Start();
}
private void update()
{
Bitmap screenCopy = new Bitmap(1, 1);
using (Graphics gdest = Graphics.FromImage(screenCopy))
{
while (true)
{
//g.CopyFromScreen(new Point(0, 0), new Point(0, 0), new Size(256, 256));
using (Graphics gsrc = Graphics.FromHwnd(IntPtr.Zero))
{
IntPtr hSrcDC = gsrc.GetHdc();
IntPtr hDC = gdest.GetHdc();
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, x, y, (int)CopyPixelOperation.SourceCopy);
gdest.ReleaseHdc();
gsrc.ReleaseHdc();
}
Color c = Color.FromArgb(screenCopy.GetPixel(0, 0).ToArgb());
label1.ForeColor = c;
}
}
}
}
}
This line uses About 10 ms.
int retval = BitBlt(hDC, 0, 0, 1, 1, hSrcDC, location.X, location.Y, (int)CopyPixelOperation.SourceCopy);

Categories