I have a C# WPF application with a Canvas. The Canvas is wider then the screen, so I use a ScrollViewer on the Canvas. And everything is working fine.
Here comes the problem:
With a much wider Canvas (width=44655), the performance of the scrollviewer is really slow. Changing the scrollposition takes a couple of seconds.
How can I increase the performance for this wide Canvas?
The Canvas I use = 44655 x 969
The width of the ScrollViewer = 1488
I've tried to place the children elements (total of 602 children elements) of the Canvas in one Bitmap, and place this one Bitmap on the same Canvas (after first removing all the children elements). This improves the performance big-time and it works as needed. But the memory-usage is huge, causing Out-Of-Memory exceptions many times. (I've also written the Bitmap as PNG to a file and this results in a file of 6MB, with 44655x969 pixels).
Is there another way to increase the scroll-performance with the Canvas? Or is there a way to reduce the memory use with the following code.
This is the code for making Bitmap from the Canvas and placing this Bitmap on the Canvas again:
double cnvsW = Cnvs.Width;
double cnvsH = realCnvsHeight;
if (double.IsNaN(cnvsW))
cnvsW = 0;
Size size = new Size(cnvsW, cnvsH);
// Measure and arrange the Canvas
Cnvs.Measure(size);
Cnvs.Arrange(new Rect(size));
// Create a render bitmap and push the canvas to it
RenderTargetBitmap renderBitmap = new RenderTargetBitmap((int)size.Width, (int)size.Height, 96d, 96d, PixelFormats.Default);//.Pbgra32);
renderBitmap.Render(Cnvs);
// Save to bitmap opbject
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Interlace = PngInterlaceOption.Off;
encoder.Frames.Add(BitmapFrame.Create(renderBitmap));
MemoryStream stream = new MemoryStream();
encoder.Save(stream);
System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(stream);
// Save to file for debugging
System.IO.File.WriteAllBytes("testCanvas.png", stream.ToArray());
// Now place the bitmap to the canvas on the screen
Cnvs.Children.Clear();
IntPtr hBitmap = bitmap.GetHbitmap();
try
{
System.Windows.Controls.Image image = new System.Windows.Controls.Image();
image.Source = System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(hBitmap, IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
Canvas.SetTop(image, 0);
Cnvs.Children.Add(image);
Cnvs.Width = size.Width;
}
finally
{
DeleteObject(hBitmap);
}
Related
This code basically takes an image and crops it according to a detected image (in this case, it detects a decentralized fingerprint, and returns a new Bitmap with the fingerprint centered and cropped).
It turns out that, depending on the image, each resulting Bitmap will have a different size (for example, my test is returning a 425x448 Bitmap, since the identified image has that size), when in fact I need the image to return with a specific size (512x512).
I've already tried to change all the height and width variables of the code, but none satisfy this desired condition. Either it creates a Bitmap with the size of 512x512 and stretches the original image (violating the original ratio), or it creates a 512x512 Bitmap with the cropped image but with a black border on the right and bottom sides.
Any hints of what can be changed or included in the code?
Edit: More clearly, I need to create a 512x512 canvas for the 425x448 image without changing the size or dimensions of the image (it should be 425x448 inside a 512x512 canvas).
private byte[] GetAndCropImage(byte[] image, IEnumerable<YoloItem> yoloItems)
{
byte[] imageRet = null;
var topYoloItem = yoloItems?.Where(x => x.Confidence >= 0.30).OrderByDescending(x => x.Confidence).First();
MemoryStream ms = new MemoryStream(image);
Bitmap src = new Bitmap(ms);
Rectangle cropRect = new Rectangle(topYoloItem.X, topYoloItem.Y, topYoloItem.Width, topYoloItem.Height);
Bitmap target = new Bitmap(cropRect.Width, cropRect.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
cropRect,
GraphicsUnit.Pixel);
}
target.SetResolution(512, 512);
ImageConverter converter = new ImageConverter();
imageRet = (byte[])converter.ConvertTo(target, typeof(byte[]));
return imageRet;
}
I need take a screenshot from a part of Canvas but because RenderTargetBitmap
doesn't support take a screenshot from a region first I should take a screenshot from total of the Canvas and after that crop it with CroppedBitmap.
But problem is size of the canvas is bigger than 20000px and when I use from RenderTargetBitmap to take a screenshot sometimes I have an error about out of memory.
Do you have any idea to take a screenshot just with a start point and an end point instead of take a screenshot from total of my canvas in WPF?
You can render a cropped area from a UI element into a bitmap by a method like shown below.
It uses a VisualBrush with an appropriate Viewbox to draw the crop into a DrawingVisual, which is subsequently drawn into a RenderTargetBitmap.
private BitmapSource RenderCrop(Visual element, Rect crop)
{
var visualBrush = new VisualBrush
{
Visual = element,
ViewboxUnits = BrushMappingMode.Absolute,
Viewbox = crop,
Stretch = Stretch.None
};
var drawingVisual = new DrawingVisual();
using (var dc = drawingVisual.RenderOpen())
{
dc.DrawRectangle(visualBrush, null, new Rect(0, 0, crop.Width, crop.Height));
}
var bitmap = new RenderTargetBitmap(
(int)Math.Round(crop.Width), (int)Math.Round(crop.Height),
96, 96, PixelFormats.Default);
bitmap.Render(drawingVisual);
return bitmap;
}
I have a WPF app which saves out a thumbnail image as png. The code works well enough but when I open the image is very blurry. The image that it grabs comes from the canvas itself. The canvas changes its width and height depending on the image I'm loading. The desired thumbnail size will be 200 x 200 (pixels).
Here is my code
public void CreateThumbail(Canvas canvas, string filename)
{
RenderTargetBitmap rtb = new RenderTargetBitmap(
(int)canvas.ActualWidth,
(int)canvas.ActualHeight,
96, //dip X
96, //dpi Y
PixelFormats.Pbgra32);
rtb.Render(canvas);
PngBitmapEncoder pngImage = new PngBitmapEncoder();
pngImage.Frames.Add(CreateResizedImage(rtb, 200, 200, 0));
using (var filestream = System.IO.File.Create(filename))
{
pngImage.Save(filestream);
}
}
private static BitmapFrame CreateResizedImage(ImageSource source, int width, int height, int margin)
{
var rect = new Rect(margin, margin, width, height);
var group = new DrawingGroup();
RenderOptions.SetBitmapScalingMode(group, BitmapScalingMode.HighQuality);
group.Children.Add(new ImageDrawing(source, rect));
var drawingVisual = new DrawingVisual();
using (var drawingContext = drawingVisual.RenderOpen())
drawingContext.DrawDrawing(group);
var resizedImage = new RenderTargetBitmap(
(int)rect.Width, (int)rect.Height,// Resized dimensions
96, 96, // Default DPI values
PixelFormats.Pbgra32); // Default pixel format
resizedImage.Render(drawingVisual);
return BitmapFrame.Create(resizedImage);
}
I saved out the image before I resize it and it looks crisp and sharp. yet when I save out the thumbnail it's ugly and blurry. What am I doing wrong? Am I over-engineering this? Many thanks in advance.
Probably you need to respect the original dimensions of the image. For instance an image that is 400x400 will downscale to 200x200 quite nicely, but an image that is 235x235 will not.
This doesn't consider images that are not square to begin with.
You might try reducing the image height and width by a good factor (I would start by halving) repeatedly until the image is smaller than 200x200 and then padding it with white or transparent.
Image processing can be quite hard. It's not something I'm an expert in either so I'd probably try a 3rd party library like this one I just found on google: https://imageprocessor.org/
Ok so here's how this works:
camera sends me an image as a writeable bitmap
client.WhenMasterFrameCaptured().ObserveOn(SynchronizationContext.Current).Subscribe(frame => UpdateMasterCameraPreview(frame));
I set that pointer to an image source(wpf Image control) in the method that this is subscribed to
if (cameraOneBitmap != null)
{
frame.Image.CopyTo(cameraOneBitmap);
}
else
{
cameraOneBitmap = frame.Image.ToWriteableBitmap();
}
cameraOneBitmap.Lock();
cameraOneBitmap.AddDirtyRect(new Int32Rect(0, 0, cameraOneBitmap.PixelWidth, cameraOneBitmap.PixelHeight));
cameraOneBitmap.Unlock();
cameraOneImage.Source = cameraOneBitmap;
The Problem: The method feeds me a bitmap that isn't the correct angle and i have no way of changing that. I must rotate it. I'm currently using a render transform in xaml. the problem is it shapes the image all weird in certain window sizes it hangs over the grid into the bottom of the window so i don't want to use that. I've tried using writeablebitmapex library but it creates a new bitmap and that gives me an out of memory exception in about 10 seconds... Is there a way to rotate this bitmap with out have to make a new one every time? Somehow the render transform does it without giving me problems. I was doing this to set the source but it still gives me an exception because it makes a new one.
public static RenderTargetBitmap RotateImage(double angle, WriteableBitmap sourceBitmap)
{
TransformedBitmap tb = new TransformedBitmap(sourceBitmap, new RotateTransform(angle));
DrawingVisual drawingVisual = new DrawingVisual();
DrawingContext drawingContext = drawingVisual.RenderOpen();
drawingContext.DrawImage(tb, new Rect(0, 0, tb.PixelWidth, tb.PixelHeight));
//drawingContext.PushTransform(new RotateTransform(270, .5, .5));
drawingContext.Close();
System.Windows.Media.Imaging.RenderTargetBitmap bmp = new System.Windows.Media.Imaging.RenderTargetBitmap(tb.PixelWidth, tb.PixelHeight, 96, 96, PixelFormats.Pbgra32);
bmp.Render(drawingVisual);
return bmp;
}
I have a situation where I want to convert some XAML to an image, so I created a RichTextBox and then took the image of it. Now problem is that words in image is blurred, any idea how I might be able to fix it?
public System.Drawing.Bitmap ConvertXamltoImage(string XamlString, int Width, int Height)
{
RichTextBox AdContentRichTextBox = new RichTextBox() { Width = Width, Height = Height };
AdContentRichTextBox.BorderThickness = new Thickness(0);
XmlReader _XmlReader = XmlReader.Create(new StringReader(XamlString));
AdContentRichTextBox.Document = XamlString;
var size = new Size(Width, Height);
AdContentRichTextBox.Measure(size);
AdContentRichTextBox.Arrange(new Rect(size));
RenderTargetBitmap bmp = new RenderTargetBitmap(Width, Height, 300, 300, PixelFormats.Pbgra32);
bmp.Render(AdContentRichTextBox);
DrawingVisual _drawingVisual = new DrawingVisual();
using (DrawingContext _drwaingContext = _drawingVisual.RenderOpen())
{
VisualBrush _visualBrush = new VisualBrush(AdContentRichTextBox);
}
PngBitmapEncoder _png = new PngBitmapEncoder();
_png.Frames.Add(BitmapFrame.Create(bmp));
System.Drawing.Bitmap _tempBitmap = null;
using (Stream _fileStream = new MemoryStream())
{
_png.Save(_fileStream);
_tempBitmap = new System.Drawing.Bitmap(_fileStream);
_fileStream.Flush();
}
return _tempBitmap;
}
Hmmmm..there could be lots of things all interacting here:
1st
"Grayscale fall back - if ClearType is disabled or one is rendering text in certain situations where the ClearType algorithm cannot be run, WPF will use a grayscale rendering algorithm to antialias the rendered text."
Rendering Text to a RenderTargetBitmap seems to be one of those situations....(the renderer switches from a hardware to a software path).
2nd
In addition NET 4 switched the default scaling algorithm from high-quality (Fant) to low-quality (Bi-Linear).....now that shouldn't come into play here as it doesn't look like you are scaling the bitmap in any way...but you never know what's going on inside. It's possible to switch the scaler back to the higher quality one.
http://www.olsonsoft.com/blogs/stefanolson/post/Workaround-for-low-quality-bitmap-resizing-in-WPF-4.aspx
3rd
You may need to take into account the parent container of the RichTextBox...see last link below, mentions it can distort the font rendering.
Problems with rendering text as bitmaps using WPF
Some ideas on how to work around this are:
render the RichTextBox at a higher resolution e.g. 600dpi, and then scale down the bitmap (probably will make no difference)
capture the screen....difficult or not practical if your visual is offscreen/obscured, etc.
See related links:
http://windowsclient.net/wpf/white-papers/wpftextclarity.aspx
WPF RenderTargetBitmap downscaling text ClearType to GreyScale
WPF RenderTargetBitmap downscaling TextRenderMode to GreyScale
WPF text rendering inconsistencies