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.
I'm working on a task that draw a string on an image.
but the result image is not same with source code that I wrote down.
following image shows the font size difference.
red text is written in NanumSquare font 18px in window paint.
and the black text date below red text is also NanumSquare font 18px. It is written with C# source code.
following is my source code. C#.
static void Main(string[] args)
{
DrawTextToImageSave("webPrint_back.png");
}
public static void DrawTextToImageSave(string path)
{
//png to bitmap
Image Dummy = Image.FromFile(path);
using (Bitmap bitmap = (Bitmap)Dummy)
{//load the image file
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
var titleFont = new Font("NanumSquareOTF ExtraBold", 25);
var bodyFont = new Font("NanumSquareOTF Regular", 25);
graphics.DrawString("DATE", titleFont, System.Drawing.Brushes.Black, new PointF(401.5f, 863.5f)); //comment 1
graphics.DrawString(DateTime.Now.ToString("yyyy.MM.dd"), bodyFont, System.Drawing.Brushes.Black, new PointF(345, 885f));
graphics.DrawString("LOCATION", titleFont, System.Drawing.Brushes.Black, new PointF(344, 919.5f));
graphics.DrawString(System.DateTime.Now.ToString("yyyyMMddHHmmss") , bodyFont, System.Drawing.Brushes.Black, new PointF(267f, 946f));
WriteableBitmap bitmapimg = Generator128Code("STACKOVERFLOW", 110, 110);
bitmapimg = resize_image(bitmapimg, 1.4); //comment 2
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapimg));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var qrBitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
graphics.DrawImage(qrBitmap, 485f, 855f);
}
bitmap.Save( "output_WebPrintBack.png", ImageFormat.Png);
}
}
see comment 1. I expect it draws exactly 18px font. but It does not.
I have also same problem on drawing qrcode with zxing.
without comment2 code I get a ~90 px qr code size.
public static WriteableBitmap Generator128Code(string contents, int width, int height)
{
if (string.IsNullOrEmpty(contents))
{
return null;
}
EncodingOptions options = null;
BarcodeWriter writer = null;
options = new QrCodeEncodingOptions
{
CharacterSet = "UTF-8",
Width = width,
Height = height,
ErrorCorrection = ErrorCorrectionLevel.H,
Margin = 0
};
writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = options
};
WriteableBitmap bitmap = writer.Write(contents);
return bitmap;
}
static WriteableBitmap resize_image(WriteableBitmap img, double scale)
{
BitmapSource source = img;
var s = new ScaleTransform(scale, scale);
var res = new TransformedBitmap(img, s);
return convert_BitmapSource_to_WriteableBitmap(res);
}
static WriteableBitmap convert_BitmapSource_to_WriteableBitmap(BitmapSource source)
{
// Calculate stride of source
int stride = source.PixelWidth * (source.Format.BitsPerPixel / 8);
// Create data array to hold source pixel data
byte[] data = new byte[stride * source.PixelHeight];
// Copy source image pixels to the data array
source.CopyPixels(data, stride, 0);
// Create WriteableBitmap to copy the pixel data to.
WriteableBitmap target = new WriteableBitmap(source.PixelWidth
, source.PixelHeight, source.DpiX, source.DpiY
, source.Format, null);
// Write the pixel data to the WriteableBitmap.
target.WritePixels(new Int32Rect(0, 0
, source.PixelWidth, source.PixelHeight)
, data, stride, 0);
return target;
}
with multiplying x 1.4 with its size, I can get similar result that I want.
why this difference has occured?
size of origin image is also 638px * 1010px. following image is origin image.
thank you for reading. and I apologize my poor English skill.
Edit
following source is executable with Console .net framework.
I retry with this source code but the result was same. :( ...
following source code is full source code.
you need png file that named "webPrint_back.png". and size 638x1010 . https://dummyimage.com/
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using ZXing;
using ZXing.Common;
using ZXing.QrCode;
using ZXing.QrCode.Internal;
using BarcodeWriter = ZXing.Presentation.BarcodeWriter;
namespace bitmapTest
{
class Program
{
static void Main(string[] args)
{
DrawTextToImageSave("webPrint_back.png");
}
public static void DrawTextToImageSave(string path)
{
//png to bitmap
Image Dummy = Image.FromFile(path);
using (Bitmap bitmap = (Bitmap)Dummy)
{//load the image file
using (Graphics graphics = Graphics.FromImage(bitmap))
{
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
var titleFont = new Font("NanumSquareOTF ExtraBold", 18);
var bodyFont = new Font("NanumSquareOTF Regular", 18);
graphics.DrawString("DATE", titleFont, System.Drawing.Brushes.Black, new PointF(401.5f, 863.5f)); //comment 1
graphics.DrawString(DateTime.Now.ToString("yyyy.MM.dd"), bodyFont, System.Drawing.Brushes.Black, new PointF(345, 885f));
graphics.DrawString("LOCATION", titleFont, System.Drawing.Brushes.Black, new PointF(344, 919.5f));
graphics.DrawString(System.DateTime.Now.ToString("yyyyMMddHHmmss") , bodyFont, System.Drawing.Brushes.Black, new PointF(267f, 946f));
WriteableBitmap bitmapimg = Generator128Code("STACKOVERFLOW", 110, 110);
bitmapimg = resize_image(bitmapimg, 1.4); //comment 2
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapimg));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var qrBitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
graphics.DrawImage(qrBitmap, 485f, 855f);
}
bitmap.Save( "output_WebPrintBack.png", ImageFormat.Png);
}
}
public static WriteableBitmap Generator128Code(string contents, int width, int height)
{
if (string.IsNullOrEmpty(contents))
{
return null;
}
EncodingOptions options = null;
BarcodeWriter writer = null;
options = new QrCodeEncodingOptions
{
CharacterSet = "UTF-8",
Width = width,
Height = height,
ErrorCorrection = ErrorCorrectionLevel.H,
Margin = 0
};
writer = new BarcodeWriter
{
Format = BarcodeFormat.QR_CODE,
Options = options
};
WriteableBitmap bitmap = writer.Write(contents);
return bitmap;
}
static WriteableBitmap resize_image(WriteableBitmap img, double scale)
{
BitmapSource source = img;
var s = new ScaleTransform(scale, scale);
var res = new TransformedBitmap(img, s);
return convert_BitmapSource_to_WriteableBitmap(res);
}
static WriteableBitmap convert_BitmapSource_to_WriteableBitmap(BitmapSource source)
{
// Calculate stride of source
int stride = source.PixelWidth * (source.Format.BitsPerPixel / 8);
// Create data array to hold source pixel data
byte[] data = new byte[stride * source.PixelHeight];
// Copy source image pixels to the data array
source.CopyPixels(data, stride, 0);
// Create WriteableBitmap to copy the pixel data to.
WriteableBitmap target = new WriteableBitmap(source.PixelWidth
, source.PixelHeight, source.DpiX, source.DpiY
, source.Format, null);
// Write the pixel data to the WriteableBitmap.
target.WritePixels(new Int32Rect(0, 0
, source.PixelWidth, source.PixelHeight)
, data, stride, 0);
return target;
}
}
}
I found the factor that makes font size is smaller than I expected.
First, source code that I posted works bad with PNG File with "24 bit depth"(without transparent background).
but PNG file with "32 bit depth"(with transparent background), Fontsize works well.
I don't know why It happened.
Second, the barcode made with zxing.net nuget. internally has padding with its border. the problem is that padding size depends on string length. longer string length makes more smaller barcode size and more bigger padding.
following source is my solution for barcode zxing.net nuget
WriteableBitmap bitmapimg = Generator128Code(StaticCommon.localConfigModel.cardBack_QRText, 110, 110);
var stream = new MemoryStream();
var encoder = new JpegBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmapimg));
encoder.Save(stream);
byte[] buffer = stream.GetBuffer();
var qrBitmap = new System.Drawing.Bitmap(new MemoryStream(buffer));
RectangleF recF = new RectangleF(new PointF(477f, 852f), new SizeF(130, 130));
//ZXING PADDING value, padding size depends on QR encoded string length, so I divide with integer 30 and use remainder
int len = StaticCommon.localConfigModel.cardBack_QRText.Length;
int pad = (int)len / 30;
if (len % 30 > 0) pad++;
RectangleF srecF = new RectangleF(pad * 6f, pad * 6f, 110f - pad * 12f, 110 - pad * 12f);
graphics.DrawImage(qrBitmap, recF, srecF, GraphicsUnit.Pixel);
I solved my problem with engineering way, but I hope someone solve this problem with theoretical way. so I remain this question unsolved.
how to resize an image without loosing its quality in c#.net?
Stream myBlob;
var encoder = new JpegEncoder();using (var output = new MemoryStream())
using (SixLabors.ImageSharp.Image<Rgba32> image = Image.Load(myBlob))
{ var divisor = image.Width / 100;
var height = Convert.ToInt32(Math.Round((decimal)(image.Height/divisor)));
image.Mutate(x => x.Resize(320, 640));
image.Save(output, encoder);
output.Position = 0;
await blockBlob.UploadFromStreamAsync(output);
}
You can use the overloaded Bitmap constructor to create the new re-sized image, as can be seen bellow:
public static Image Resize(Image image, Size size)
{
return (Image)(new Bitmap(image, size));
}
And you call it using:
var result = Resize(image, new Size(320,640));
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
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;
}
}