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
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);
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);
}
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;
}
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.
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);