Screen.PrimaryScreen.Bounds.Size on WPF - c#

I was examining a program code. It was writen in WinForms, but i tried to write it in WPF. Here is my code:
Graphics graphics = null;
var w = System.Windows.SystemParameters.PrimaryScreenWidth;
var h = System.Windows.SystemParameters.PrimaryScreenHeight;
graphics.CopyFromScreen(location.X, location.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
Visual Studio says "The name 'Screen' does not exist in the current context". What is the problem?

It is obvious, because Screen is inside System.Windows.Forms and you do not have access to it from WPF application.
I suppose you are trying to take screenshot so it meight help you in WPF:
private void TakeScreenShot()
{
double Left = SystemParameters.VirtualScreenLeft;
double Top = SystemParameters.VirtualScreenTop;
double ScreenWidth = SystemParameters.VirtualScreenWidth;
double ScreenHeight = SystemParameters.VirtualScreenHeight;
using (System.Drawing.Bitmap bmpScreen = new System.Drawing.Bitmap((int)ScreenWidth, (int)ScreenHeight))
{
using (System.Drawing.Graphics graphic = System.Drawing.Graphics.FromImage(bmpScreen))
{
graphic.CopyFromScreen((int)Left, (int)Top, 0, 0, bmpScreen.Size);
bmpScreen.Save(#"D:\bitmap.bmp");
IMG.Source = BitmapToImageSource(bmpScreen); // show bitmap in IMG (Image control)
}
}
}
BitmapImage BitmapToImageSource(Bitmap bitmap)
{
using (MemoryStream memory = new MemoryStream())
{
bitmap.Save(memory, System.Drawing.Imaging.ImageFormat.Bmp);
memory.Position = 0;
BitmapImage bitmapimage = new BitmapImage();
bitmapimage.BeginInit();
bitmapimage.StreamSource = memory;
bitmapimage.CacheOption = BitmapCacheOption.OnLoad;
bitmapimage.EndInit();
return bitmapimage;
}
}
you might need to add a reference to System.Drawing.dll
Updated the answer based on Comment from #Erno de Weerd.
Besides a method to show a bitmap in the image control also added

Related

WPF C# Stride it too low for CopyPixels method

I have to convert a BitmapSource into a BitmapImage because of the Interface I am using. If I display the BitmapSource to an Image view, it works. but when I try and convert the BitmapSource into a BitmapImage, I get this error: "The parameter value cannot be less than '3686400'.\r\nParameter name: buffer" string
My PixelFormat is Bgr24
Here is my code :
void GetFrame(BitmapSource image)
{
try
{
BitmapSourceImageChanged?.Invoke(image);
BitmapImage tempImage = new BitmapImage();
//tempImage = image as BitmapImage;
// Frame = tempImage; var temp = image.Format;
int imagePixelHeight = image.PixelHeight;
int imagePixelWidth = image.PixelWidth;
byte[] pixels = new byte[imagePixelHeight * imagePixelWidth];
int stride = (imagePixelWidth * image.Format.BitsPerPixel +7) / 8;
image.CopyPixels(pixels,stride,0);
Frame = BitmapImageFromArray(pixels, image.PixelWidth, image.PixelHeight);
if (Frame == null)
{
int stop = 0;
}
}
catch (Exception err)
{
Error = err.Message;
}
}
BitmapImage BitmapImageFromArray(byte[] transfer, int imageWidth, int imageHeight)
{
BitmapImage image = new BitmapImage();
try
{
using (
// Convert byte array to BitmapImage()
MemoryStream stream = new MemoryStream(transfer))
{
image.BeginInit();
image.DecodePixelWidth = imageWidth;
image.DecodePixelHeight = imageHeight;
image.StreamSource = stream;
image.CacheOption = BitmapCacheOption.OnLoad;
image.EndInit();
}
}
catch (Exception err)
{
}
return image;
}
My code throws the exception at the CopyPixels method.
Is my stride equation not correct?
Your stride calculation is ok, but not the calculation of the buffer size.
The buffer size is the product of the number of scan lines and the bytes per scan line, i.e. the stride:
int stride = (imagePixelWidth * image.Format.BitsPerPixel + 7) / 8;
byte[] pixels = new byte[imagePixelHeight * stride]; // here
However, your code will still not work, since you can not decode a BitmapImage from a raw pixel buffer. Decoding means to read an encoded bitmap frame, for example a JPG or PNG.
In order to decode a BitmapImage, you would have to create an encoded frame first:
public static BitmapImage BitmapSourceToBitmapImage(BitmapSource bitmapSource)
{
var bitmapImage = bitmapSource as BitmapImage;
if (bitmapImage == null)
{
bitmapImage = new BitmapImage();
var encoder = new BmpBitmapEncoder(); // or some other encoder
encoder.Frames.Add(BitmapFrame.Create(bitmapSource));
using (var stream = new MemoryStream())
{
encoder.Save(stream);
stream.Position = 0;
bitmapImage.BeginInit();
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.StreamSource = stream;
bitmapImage.EndInit();
}
}
return bitmapImage;
}
That said, there should actually never be a need to have a method like the above at all. All properties that are relevant for a bitmap in WPF are declared in the BitmapSource class. The additional properties in BitmapImage are only used during creation, but have no relevance afterwards.

How to save bmp (image) to file

I want to Video File thumbnail. Get Video path and convert to Image. Then convert to bmp, and save the bmp as an image file. If this is possible, please show me a way.
private void add_Video_Image(string sFullname_Path_of_Video)
{
//*create mediaplayer in memory and jump to position
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.MediaOpened += new EventHandler(mediaplayer_OpenMedia);
mediaPlayer.ScrubbingEnabled = true;
mediaPlayer.Open(new Uri(sFullname_Path_of_Video));
mediaPlayer.Position = TimeSpan.FromSeconds(0);
}
private void mediaplayer_OpenMedia(object sender, EventArgs e)
{
MediaPlayer mediaPlayer = sender as MediaPlayer;
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawVideo(mediaPlayer, new Rect(0, 0, 160, 100));
drawingContext.Close();
double dpiX = 1 / 200;
double dpiY = 1 / 200;
RenderTargetBitmap bmp = new RenderTargetBitmap(160, 100, dpiX, dpiY, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
Image newImage = new Image();
newImage.Source = bmp;
newImage.Stretch = Stretch.Uniform;
newImage.Height = 100;
//save bmp to image
}
Please check this link. It holds the answer to your question: Easiest way of saving wpf Image control to a file
You can do the save prior to setting the 'RenderTargetBitmap' to the 'Image Control'.

Error in Quantization of Image using NQUANT

I recently acquired the NuGet Package Nquant.
I plan to use this to reduce the file size of the bitmap and save it into PNG. But I get this error:
The image you are attempting to quantize does not contain a 32 bit ARGB palette. This image has a bit depth of 8 with 256 colors.
Does anyone here has used Nquant? And have you encountered this error and how did you fix it?
My code for your reference:
var bitmap = new Bitmap(width, jbgsize / height, PixelFormat.Format8bppIndexed);
ColorPalette pal = bitmap.Palette;
for (int i = 0; i <= 255; i++)
{
// create greyscale color table
pal.Entries[i] = Color.FromArgb(i, i, i);
}
bitmap.Palette = pal; // you need to re-set this property to force the new ColorPalette
var bitmap_data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
Marshal.Copy(output, 0, bitmap_data.Scan0, output.Length);
bitmap.UnlockBits(bitmap_data);
MemoryStream stream = new MemoryStream();
var quantizer = new WuQuantizer();
using(var bmp = new Bitmap(bitmap))
{
using (var quantized = quantizer.QuantizeImage(bitmap))
{
quantized.Save(stream, ImageFormat.Png);
}
}
byteArray = stream.ToArray();
return byteArray.Concat(output).ToArray();
You can convert your image to Format32bppPArgb and then Quantize it.
This is my working example of that decrease image size for ~3 times.
public static byte[] CompressImageStream(byte[] imageStream)
{
using (var ms = new MemoryStream(imageStream))
using (var original = new Bitmap(ms))
using (var clonedWith32PixelsFormat = new Bitmap(
original.Width,
original.Height,
PixelFormat.Format32bppPArgb))
{
using (Graphics gr = Graphics.FromImage(clonedWith32PixelsFormat))
{
gr.DrawImage(
original,
new Rectangle(0, 0, clonedWith32PixelsFormat.Width, clonedWith32PixelsFormat.Height));
}
using (Image compressedImage = new WuQuantizer().QuantizeImage(clonedWith32PixelsFormat))
{
return ImageToByteArray(compressedImage);
}
}
}
public static byte[] ImageToByteArray(Image image)
{
if (image == null)
{
throw new ArgumentNullException(nameof(image));
}
using (var stream = new MemoryStream())
{
image.Save(stream, ImageFormat.Png);
return stream.ToArray();
}
}

Image resize when rotate

I'm trying to rotate the image.. I have a pictureBox 369x276. But when I rotate, this size decrease.
The pictureBox sizeMode is PictureBoxSizeMode.StretchImage
here is my code:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
float angle = 90;
var newBitmap = new Bitmap(oldBitmap.Width, oldBitmap.Height);
var graphics = Graphics.FromImage(newBitmap);
graphics.TranslateTransform((float)oldBitmap.Width / 2, (float)oldBitmap.Height / 2);
graphics.RotateTransform(angle);
graphics.TranslateTransform(-(float)oldBitmap.Width / 2, -(float)oldBitmap.Height / 2);
graphics.DrawImage(oldBitmap, new Point(0, 0));
pictureBox1.Image = newBitmap;
Just use RotateFlip:
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;
oldBitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
pictureBox1.Image = oldBitmap;
As #Dan-o has pointed out, this allows a rotation of any of the degree's in the System.Drawing.RotateFlipType enum.
To rotate a Bitmap any angle without losing the size, you could do the following, but it's a bit convoluted!
One - Add the WriteableBitmapEx library to your project
Two - Add the XAML, WindowsBase and PresentationCore libraries to your project
Three - Use the following to rotate your Bitmap any amount of degrees:
class Program
{
static void Main(string[] args)
{
Bitmap oldBitmap = (Bitmap)pictureBox1.Image;;
var bitmapAsWriteableBitmap = new WriteableBitmap(BitmapToBitmapImage(oldBitmap));
bitmapAsWriteableBitmap.RotateFree(23);
var rotatedImageAsMemoryStream = WriteableBitmapToMemoryStream(bitmapAsWriteableBitmap);
oldBitmap = new Bitmap(rotatedImageAsMemoryStream);
}
public static BitmapImage BitmapToBitmapImage(Bitmap bitmap)
{
var memStream = BitmapToMemoryStream(bitmap);
return MemoryStreamToBitmapImage(memStream);
}
public static MemoryStream BitmapToMemoryStream(Bitmap image)
{
var memoryStream = new MemoryStream();
image.Save(memoryStream, ImageFormat.Bmp);
return memoryStream;
}
public static BitmapImage MemoryStreamToBitmapImage(MemoryStream ms)
{
ms.Position = 0;
var bitmap = new BitmapImage();
bitmap.BeginInit();
bitmap.StreamSource = ms;
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.EndInit();
bitmap.Freeze();
return bitmap;
}
private static MemoryStream WriteableBitmapToMemoryStream(WriteableBitmap writeableBitmap)
{
var ms = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(writeableBitmap));
encoder.Save(ms);
return ms;
}
}
Pain in the ass, but works!
The smaller image size is to be expected. I've never figured out why, but the Graphics.DrawImage really only works if you provide it not only a start location, but also a size. One of the overloads allows you to include size.

How can I create a 4-bit PNG with C#?

I am trying to create a 4-bit PNG file in C# but my code does not work.
Here is the code:
Bitmap bmp = new Bitmap(200, 50, PixelFormat.Format4bppIndexed);
string f = bmp.PixelFormat.ToString();
Graphics gImage = Graphics.FromImage(bmp);
gImage.FillRectangle(Brushes.Red, 0, 0, bmp.Width - 20, bmp.Height - 20);
gImage.DrawRectangle(Pens.White, 0, 0, bmp.Width - 20, bmp.Height - 20);
gImage.DrawString("Test", SystemFonts.DefaultFont, Brushes.White, 5, 8);
bmp.Save("C:\\buttons_normal1.png",ImageFormat.Png);
The code throws an exception at Graphics gImage line due to PixelFormat set to Format4bppIndexed. I saw a solution here suggesting that the final bitmap can be converted to 4-bit, but that code never worked for me.
Any suggestions?
The problem is that you aren't allowed to create a Graphics object with an indexed pixel format.
One solution would be to create a Graphics object in a different format to do your drawing, and create an empty Bitmap in PixelFormat.Format4bppIndexed format, and copy each pixel from one image to the other.
Create a non-4bit, and then convert to 4bit using the System.Windows.Media.Imaging library:
public void to4bit(Bitmap sourceBitmap, Stream outputStream)
{
BitmapImage myBitmapImage = ToBitmapImage(sourceBitmap);
FormatConvertedBitmap fcb = new FormatConvertedBitmap();
fcb.BeginInit();
myBitmapImage.DecodePixelWidth = sourceBitmap.Width;
fcb.Source = myBitmapImage;
fcb.DestinationFormat = System.Windows.Media.PixelFormats.Gray4;
fcb.EndInit();
PngBitmapEncoder bme = new PngBitmapEncoder();
bme.Frames.Add(BitmapFrame.Create(fcb));
bme.Save(outputStream);
}
private BitmapImage ToBitmapImage(Bitmap sourceBitmap)
{
using (var memory = new MemoryStream())
{
sourceBitmap.Save(memory, ImageFormat.Png);
memory.Position = 0;
var bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = memory;
bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
bitmapImage.EndInit();
return bitmapImage;
}
}

Categories

Resources