Opening a file that's already in use - c#

using (System.IO.FileStream fs = File.Open(GetCurrentWallpaper(), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
I'm writing an app that needs to open the current wallpaper like this every time it's changed.
I first access the registry to get the wallpaper's path (GetCurrentWallpaper), and use a FileSystemWatcher to do stuff with the wallpaper when it's changed.
Oddly, it only works once. If the wallpaper is accessed a second time(it doesn't matter how long I wait), my app crashed with an IOException telling me that I can't access the file because it's already in use.
If I restart the app, it can access the file again, but, as mentioned above, only once. Else it crashes.
Is there anything I can do in order to gain access to that file?
Edit: More code:
using (System.IO.FileStream fs = File.Open(GetCurrentWallpaper(), FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
using (Bitmap orig = (Bitmap)Bitmap.FromStream(fs, true, false)) {
int width = Convert.ToInt32(orig.Width / 3);
int height = Convert.ToInt32(orig.Height / 3);
Rectangle rect = new Rectangle(0, 0, width, height);
using (Bitmap bmp = new Bitmap(width, height)) {
using (Graphics bmpg = Graphics.FromImage(bmp)) {
col = ColorHelper.CalculateAverageColor(bmp, true, 20);
fs.Flush();
fs.Close();
}
}
}
}
//this is in the ColorHelper class
public static System.Drawing.Color CalculateAverageColor(Bitmap bm, bool dropPixels, int colorDiff) {
int width = bm.Width;
int height = bm.Height;
int red = 0;
int green = 0;
int blue = 0;
int minDiversion = colorDiff;
int dropped = 0;
long[] totals = new long[] { 0, 0, 0 };
int bppModifier = bm.PixelFormat == System.Drawing.Imaging.PixelFormat.Format24bppRgb ? 3 : 4;
BitmapData srcData = bm.LockBits(new System.Drawing.Rectangle(0, 0, bm.Width, bm.Height), ImageLockMode.ReadOnly, bm.PixelFormat);
int stride = srcData.Stride;
IntPtr Scan0 = srcData.Scan0;
unsafe {
byte* p = (byte*)(void*)Scan0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int idx = (y * stride) + x * bppModifier;
red = p[idx + 2];
green = p[idx + 1];
blue = p[idx];
if (dropPixels) {
if (Math.Abs(red - green) > minDiversion || Math.Abs(red - blue) > minDiversion || Math.Abs(green - blue) > minDiversion) {
totals[2] += red;
totals[1] += green;
totals[0] += blue;
} else {
dropped++;
}
} else {
totals[2] += red;
totals[1] += green;
totals[0] += blue;
}
}
}
}
int count = width * height - dropped;
int avgR;
int avgG;
int avgB;
if (count > 0) {
avgR = (int)(totals[2] / count);
avgG = (int)(totals[1] / count);
avgB = (int)(totals[0] / count);
} else {
avgR = (int)(totals[2]);
avgG = (int)(totals[1]);
avgB = (int)(totals[0]);
}
bm.UnlockBits(srcData);
return System.Drawing.Color.FromArgb(avgR, avgG, avgB);
}

If you use fs.Close() than you will release the file for further read operations.

Related

How to convert the method using getpixel and setpixel to be faster using lockbits?

public void ReadSetPixels(Bitmap image1, Bitmap image2)
{
int tolerance = 64;
for (int x = 0; x < image1.Width; x++)
{
for (int y = 0; y < image1.Height; y++)
{
Color pixelColor = image1.GetPixel(x, y);
// just average R, G, and B values to get gray. Then invert by 255.
int invertedGrayValue = 255 - (int)((pixelColor.R + pixelColor.G + pixelColor.B) / 3);
if (invertedGrayValue > tolerance) { invertedGrayValue = 255; }
// this keeps the original pixel color but sets the alpha value
image1.SetPixel(x, y, Color.FromArgb(invertedGrayValue, pixelColor));
}
}
// composite image1 on top of image2
using (Graphics g = Graphics.FromImage(image2))
{
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(image1, new Point(0, 0));
}
image2.Save(#"d:\mynewbmp.bmp");
image1.Dispose();
image2.Dispose();
}
using it
RadarPixels rp = new RadarPixels();
rp.ReadSetPixels(new Bitmap(#"d:\myimage1.png"),
new Bitmap(#"d:\clean_radar_image123.bmp"));
I looked on the example in the docs at : https://learn.microsoft.com/en-us/dotnet/api/system.drawing.bitmap.lockbits?redirectedfrom=MSDN&view=dotnet-plat-ext-7.0#overloads
but not sure how to implement it with my method.
UPDATE :
This is what I have tried so far :
created a new method :
public unsafe void Test(Bitmap Image1, Bitmap Image2)
{
int tolerance = 64;
int width = Image1.Width;
int height = Image1.Height;
//TODO determine bytes per pixel
int bytesPerPixel = 4; // we assume that image is Format32bppArgb
int maxPointerLenght = width * height * bytesPerPixel;
int stride = width * bytesPerPixel;
byte R, G, B, A;
BitmapData bData = Image1.LockBits(
new System.Drawing.Rectangle(0, 0, Image1.Width, Image1.Height),
ImageLockMode.ReadWrite, Image1.PixelFormat);
byte* scan0 = (byte*)bData.Scan0.ToPointer();
IntPtr ptr = bData.Scan0;
int bytes = Math.Abs(bData.Stride) * Image1.Height;
byte[] rgbValues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
for (int i = 0; i < maxPointerLenght; i += 4)
{
B = scan0[i + 0];
G = scan0[i + 1];
R = scan0[i + 2];
A = scan0[i + 3];
int invertedGrayValue = 255 - (int)((R + G + B) / 3);
if (invertedGrayValue > tolerance) { invertedGrayValue = 255; }
rgbValues[i] = (byte)invertedGrayValue;
}
Image1.UnlockBits(bData);
using (Graphics g = Graphics.FromImage(Image2))
{
g.CompositingMode = CompositingMode.SourceOver;
g.CompositingQuality = CompositingQuality.HighQuality;
g.DrawImage(Image1, new Point(0, 0));
}
Image2.Save(#"d:\mynewbmp.bmp");
Image2.Dispose();
}
but the result image is not as before with the method at the top with the get/set pixel. why it's not making the overlay like the method before ?
the result image :

How do I display the bitmap image in the picture box?

Store an image with the ".raw" extension in a two-dimensional byte array. Convert it to bitmap. I want to show this in the picture box, but if I run it with the code below, I get an error that the parameter is wrong.
Width and height are obtained from the information provided by the header file.
I wonder what I'm doing wrong.
string filename = #"test.raw";
byte[] rawBytes = File.ReadAllBytes(filename);
int bytePixel = 2;
int width = samples*bytePixel;
int height = lines;
byte[,] rawData = new byte[height, width];
int counter = new int();
for(int i = 0; i < height; i++)
{
for(int j = 0; j < width; j++, counter++)
{
rawData[i, j] = rawBytes[counter];
}
}
Bitmap bitmapImage = new Bitmap(width, height, PixelFormat.Format16bppGrayScale);
BitmapData bitmapImageData = bitmapImage.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, PixelFormap.Format16bppGrayScale);
unsafe
{
byte* pointer = (byte*)bitmapImageData.Scan0.ToPointer();
for(int y = 0; y < height; y++)
{
for(int x = 0; x < width; x++, pointer++)
{
*pointer = rawData[y, x];
}
}
}
bitmapImage.UnlockBits(bitmapImageData);
pictureBox1.Image = bitmapImage;
Please give me some advice.
I can't figure out what's wrong but if you just want to see byte array result on screen, this func will make bmp file with IntPtr. Hope it helps.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var fs = new FileStream("test.bmp", FileMode.Open);
Bitmap bitmap = new Bitmap(800, 600, PixelFormat.Format16bppGrayScale);
BitmapData bitmapdata = bitmap.LockBits(new Rectangle(0, 0, 800, 600), ImageLockMode.WriteOnly, PixelFormat.Format16bppGrayScale);
unsafe
{
byte* p = (byte*)bitmapdata.Scan0.ToPointer();
for (int i = 0; i < 600; i++)
{
for (int j = 0; j < 800; j++)
{
*p = (byte)(i * j); p++;
}
}
}
FileSaveBMP($"{DateTime.Now.ToString("yyyyMMddHHmmssfff")}.bmp", bitmapdata.Scan0, new CRect() { Width = 800, Height = 600 }, 800);
bitmap.UnlockBits(bitmapdata);
//pictureBox1.Image = bitmap;
}
private unsafe void FileSaveBMP(string sFile, IntPtr ptr, CRect rect, int w, int p_nByte = 1)
{
FileStream fs = new FileStream(sFile, FileMode.Create, FileAccess.Write);
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(Convert.ToUInt16(0x4d42));
if (p_nByte == 1)
{
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(54 + 1024 + p_nByte * 1000 * 1000));
else bw.Write(Convert.ToUInt32(54 + 1024 + p_nByte * (Int64)rect.Width * (Int64)rect.Height));
}
else if (p_nByte == 3)
{
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(54 + p_nByte * 1000 * 1000));//uint bfSize = br.ReadUInt32();
else bw.Write(Convert.ToUInt32(54 + p_nByte * (Int64)rect.Width * (Int64)rect.Height));//uint bfSize = br.ReadUInt32();
}
//image 크기 bw.Write(); bmfh.bfSize = sizeof(14byte) + nSizeHdr + rect.right * rect.bottom;
bw.Write(Convert.ToUInt16(0)); //reserved // br.ReadUInt16();
bw.Write(Convert.ToUInt16(0)); //reserved //br.ReadUInt16();
if (p_nByte == 1)
bw.Write(Convert.ToUInt32(1078));
else if (p_nByte == 3)
bw.Write(Convert.ToUInt32(54));//uint bfOffBits = br.ReadUInt32();
bw.Write(Convert.ToUInt32(40));// uint biSize = br.ReadUInt32();
bw.Write(Convert.ToInt32(rect.Width));// nWidth = br.ReadInt32();
bw.Write(Convert.ToInt32(rect.Height));// nHeight = br.ReadInt32();
bw.Write(Convert.ToUInt16(1));// a = br.ReadUInt16();
bw.Write(Convert.ToUInt16(8 * p_nByte)); //byte // nByte = br.ReadUInt16() / 8;
bw.Write(Convert.ToUInt32(0)); //compress //b = br.ReadUInt32();
if ((Int64)rect.Width * (Int64)rect.Height > Int32.MaxValue) bw.Write(Convert.ToUInt32(1000 * 1000));// b = br.ReadUInt32();
else bw.Write(Convert.ToUInt32((Int64)rect.Width * (Int64)rect.Height));// b = br.ReadUInt32();
bw.Write(Convert.ToInt32(0));//a = br.ReadInt32();
bw.Write(Convert.ToInt32(0));// a = br.ReadInt32();
bw.Write(Convert.ToUInt32(256)); //color //b = br.ReadUInt32();
bw.Write(Convert.ToUInt32(256)); //import // b = br.ReadUInt32();
if (p_nByte == 1)
{
for (int i = 0; i < 256; i++)
{
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(i));
bw.Write(Convert.ToByte(255));
}
}
if (rect.Width % 4 != 0)
{
rect.Right += 4 - rect.Width % 4;
}
byte[] aBuf = new byte[p_nByte * rect.Width];
for (int i = rect.Height - 1; i >= 0; i--)
{
Marshal.Copy((IntPtr)((long)ptr + rect.Left + ((long)i + (long)rect.Top) * w * p_nByte), aBuf, 0, rect.Width * p_nByte);
bw.Write(aBuf);
}
bw.Close();
fs.Close();
}
}
public class CRect
{
public int Left
{
get; set;
}
public int Right
{
get; set;
}
public int Top
{
get; set;
}
public int Bottom
{
get; set;
}
public int Width
{
get; set;
}
public int Height
{
get; set;
}
}
Above code creates image file like this.

GDI+ Error only occuring on windows XP

I have some c# code that works fine on Vista & Windows 7 but throws a GDI+ Error on Windows XP (with service pack 3 installed).
Error thrown on XP
System.Runtime.INteropServices.ExternalException(0x80004005): A generic error occured in GDI+
at System.Drawing.Graphics.CheckErrorStatus(Int32Status)
at System.Drawing.Graphics.DrawImage(Image image, Int32 x, Int32 y)
At System.Drawing.Graphics.DrawImageUnscaled(Image image, Int32 x,Int 32 y)
at mysolution.Core.ImageTools.ConvertToBitonal(Bitmap orginal, Int32 threshold)
Code breaks on this line:
using (var g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0); // Error Is Thrown Here
}
WpFAppTestingConvertToBitonalCS
....
Below is the full function I'm calling.
public static Bitmap ConvertToBitonal(Bitmap original, int threshold)
{
Bitmap source;
// If original bitmap is not already in 32 BPP, ARGB format, then convert
if (original.PixelFormat != PixelFormat.Format32bppArgb)
{
source = new Bitmap(original.Width, original.Height, PixelFormat.Format32bppArgb);
source.SetResolution(original.HorizontalResolution, original.VerticalResolution);
using (var g = Graphics.FromImage(source))
{
g.DrawImageUnscaled(original, 0, 0);
}
}
else
{
source = original;
}
// Lock source bitmap in memory
var sourceData = source.LockBits(new Rectangle(0, 0, source.Width, source.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
// Copy image data to binary array
var imageSize = sourceData.Stride * sourceData.Height;
var sourceBuffer = new byte[imageSize];
Marshal.Copy(sourceData.Scan0, sourceBuffer, 0, imageSize);
// Unlock source bitmap
source.UnlockBits(sourceData);
// Create destination bitmap
var destination = new Bitmap(source.Width, source.Height, PixelFormat.Format1bppIndexed);
destination.SetResolution(original.HorizontalResolution, original.VerticalResolution);
// Lock destination bitmap in memory
var destinationData = destination.LockBits(new Rectangle(0, 0, destination.Width, destination.Height), ImageLockMode.WriteOnly, PixelFormat.Format1bppIndexed);
// Create destination buffer
imageSize = destinationData.Stride * destinationData.Height;
var destinationBuffer = new byte[imageSize];
var sourceIndex = 0;
var destinationIndex = 0;
var pixelTotal = 0;
byte destinationValue = 0;
var pixelValue = 128;
var height = source.Height;
var width = source.Width;
// Iterate lines
for (var y = 0; y < height; y++)
{
sourceIndex = y * sourceData.Stride;
destinationIndex = y * destinationData.Stride;
destinationValue = 0;
pixelValue = 128;
// Iterate pixels
for (var x = 0; x < width; x++)
{
// Compute pixel brightness (i.e. total of Red, Green, and Blue values) - Thanks murx
// B G R
pixelTotal = sourceBuffer[sourceIndex] + sourceBuffer[sourceIndex + 1] + sourceBuffer[sourceIndex + 2];
if (pixelTotal > threshold)
{
destinationValue += (byte)pixelValue;
}
if (pixelValue == 1)
{
destinationBuffer[destinationIndex] = destinationValue;
destinationIndex++;
destinationValue = 0;
pixelValue = 128;
}
else
{
pixelValue >>= 1;
}
sourceIndex += 4;
}
if (pixelValue != 128)
{
destinationBuffer[destinationIndex] = destinationValue;
}
}
// Copy binary image data to destination bitmap
Marshal.Copy(destinationBuffer, 0, destinationData.Scan0, imageSize);
// Unlock destination bitmap
destination.UnlockBits(destinationData);
// Dispose of source if not originally supplied bitmap
if (source != original)
{
source.Dispose();
}
// Return
return destination;
}
I hate GDI+ exception handling, the only error it fires is A generic error occured in GDI+, Try this
Bitmap source = new Bitmap(0, 0);
using (Graphics g = Graphics.FromImage(source))
{ g.Clear(Color.White);
g.Graphics.DrawImageUnscaled(your original source,0,0);
}

Bitmap manipulation in Windows Phone: How to deal with GetPixel, SetPixel, PixelFormat, etc?

im trying to convert one of fully functional desktop apps to a Windows phone app, i was managed to convert most of the things..but one function is not working due to some syntax error.anyone know replacements for
GetPixel,
FromArgb,
SetPixel,
MemoryStream,
Marshal,
ImageLockMode,
PixelFormat
the code is this
private void tresh()
{
int hight = image1.Source.Height;
int width = image1.Source.Width;
BitmapImage img = new BitmapImage(image1.Source);
BitmapImage newImg = new BitmapImage(width, hight);
int threshold = 0;
for (int i = 0; i < hight; i++)
{
for (int j = 0; j < width; j++)
{
int grayScale = (int)((img.GetPixel(j, i).R * 0.3) + (img.GetPixel(j, i).G * 0.59) + (img.GetPixel(j, i).B * 0.11));
Color nc = Color.FromArgb(grayScale, grayScale, grayScale);
newImg.SetPixel(j, i, nc);
}
}
image1.Source = newImg;
MemoryStream ms = new MemoryStream();
newImg.Save(ms, ImageFormat.Bmp);
byte[] bmpBytes = ms.GetBuffer();
threshold = getOtsuNumber(bmpBytes);
byte[] newBytArr = new byte[bmpBytes.Length];
for (int i = 0; i < bmpBytes.Length; i++)
{
if ((0xFF & bmpBytes[i]) >= threshold)
{
newBytArr[i] = ((byte)255);
}
else
{
newBytArr[i] = ((byte)0);
}
}
BitmapImage oldBmp = newImg;
width = oldBmp.Width;
int height = oldBmp.Height;
BitmapData oldData = oldBmp.LockBits(
new Rectangle(0, 0, oldBmp.Width, oldBmp.Height),
ImageLockMode.WriteOnly,
oldBmp.PixelFormat);
int length = oldData.Stride * oldBmp.Height;
byte[] stream = new byte[length];
Marshal.Copy(oldData.Scan0, stream, 0, length);
oldBmp.UnlockBits(oldData);
BitmapImage bmp = new Bitmap(width, height, oldBmp.PixelFormat);
BitmapData bmpData = bmp.LockBits(
new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly,
bmp.PixelFormat);
for (int n = 0; n < length; n++)
{
if ((0xFF & stream[n]) >= 57)
{
Marshal.WriteByte(bmpData.Scan0, n, ((byte)255));
}
else
{
Marshal.WriteByte(bmpData.Scan0, n, ((byte)0));
}
}
bmp.UnlockBits(bmpData);
image1.Source = bmp;
}
you have to change the type int into byte
By using this WriteableBitmapEx GetPixel and SetPixel method extensions can be used

Why some pictures are are crooked aftes using my function?

struct BitmapDataAccessor
{
private readonly byte[] data;
private readonly int[] rowStarts;
public readonly int Height;
public readonly int Width;
public BitmapDataAccessor(byte[] data, int width, int height)
{
this.data = data;
this.Height = height;
this.Width = width;
rowStarts = new int[height];
for (int y = 0; y < Height; y++)
rowStarts[y] = y * width;
}
public byte this[int x, int y, int color] // Maybe use an enum with Red = 0, Green = 1, and Blue = 2 members?
{
get { return data[(rowStarts[y] + x) * 3 + color]; }
set { data[(rowStarts[y] + x) * 3 + color] = value; }
}
public byte[] Data
{
get { return data; }
}
}
public static byte[, ,] Bitmap2Byte(Bitmap obraz)
{
int h = obraz.Height;
int w = obraz.Width;
byte[, ,] wynik = new byte[w, h, 3];
BitmapData bd = obraz.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
int bytes = Math.Abs(bd.Stride) * h;
byte[] rgbValues = new byte[bytes];
IntPtr ptr = bd.Scan0;
System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
BitmapDataAccessor bda = new BitmapDataAccessor(rgbValues, w, h);
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
wynik[j, i, 0] = bda[j, i, 2];
wynik[j, i, 1] = bda[j, i, 1];
wynik[j, i, 2] = bda[j, i, 0];
}
}
obraz.UnlockBits(bd);
return wynik;
}
public static Bitmap Byte2Bitmap(byte[, ,] tablica)
{
if (tablica.GetLength(2) != 3)
{
throw new NieprawidlowyWymiarTablicyException();
}
int w = tablica.GetLength(0);
int h = tablica.GetLength(1);
Bitmap obraz = new Bitmap(w, h, PixelFormat.Format24bppRgb);
for (int i = 0; i < w; i++)
{
for (int j = 0; j < h; j++)
{
Color kol = Color.FromArgb(tablica[i, j, 0], tablica[i, j, 1], tablica[i, j, 2]);
obraz.SetPixel(i, j, kol);
}
}
return obraz;
}
Now, if I do:
private void btnLoad_Click(object sender, EventArgs e)
{
if (dgOpenFile.ShowDialog() == DialogResult.OK)
{
try
{
Bitmap img = new Bitmap(dgOpenFile.FileName);
byte[, ,] tab = Grafika.Bitmap2Byte(img);
picture.Image = Grafika.Byte2Bitmap(tab);
picture.Size = img.Size;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
Most of pictures are handled correctly butsome not.
Example of picture that doesn't work:
(source: ifotos.pl)
It produce following result (this is only fragment of picture) :
(source: ifotos.pl)
Why is that?
You need to account for BitmapData.Stride when you access the data.
EDIT:
Here is a solution that I use to copy a DirectX surface to a Bitmap. The idea is the same, but you'll need to modify it slightly. I copy one scanline of the image at a time with a call to RtlMoveMemory (P/Invoke to kernel32.dll)
//// Snippet
int pitch;
int bytesPerPixel = 4;
Rectangle lockRectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);
// Lock the bitmap
GraphicsStream surfacedata = surface.LockRectangle(LockFlags.ReadOnly, out pitch);
BitmapData bitmapdata = bitmap.LockBits(lockRectangle, ImageLockMode.WriteOnly, PixelFormat.Format32bppRgb);
// Copy surface to bitmap
for (int scanline = 0; scanline < bitmap.Height; ++scanline)
{
byte* dest = (byte*)bitmapdata.Scan0 + (scanline * bitmap.Width * bytesPerPixel);
byte* source = (byte*)surfacedata.InternalData + (scanline * pitch);
RtlMoveMemory(new IntPtr(dest), new IntPtr(source), (bitmap.Width * bytesPerPixel));
}
////
EDIT #2:
Check this out: Stride/Pitch Tutorial
It is all aimed at DirectX but the concept is the same.
It seems the memory allocated for bitmaps must be aligned on a 32-bit boundary and so there is possibly padding on some of the images due to their size. As you have a 24-bit pixel here then some line widths will end on a 32-bit others will not. You need to use the following formula to work out the padding being used and then account for it:
int padding = bd.Stride - (((w * 24) + 7) / 8);
You might want to load your byte array using GetPixel(x,y) rather than going through the whole transform to byte array before you start reading pixels.
Thanx to #Lazarus and tbridge I managed how to do this.
First we need to calculate padding in Bitmap2Byte:
int padding = bd.Stride - (((w * 24) + 7) / 8);
and pass it to BitmapDataAccessor and modify the line
this.Width = width;
to
this.Width = width + (4-padding)%4;
That's all. Thanx guys.

Categories

Resources