How to decode an image from a stream into an existing image

I have an application that plays audio using NAudio. A well-known limitation with NAudio is that every time the garbage collector starts, every thread pauses until it is executed.

The application is working fine, all the GC runs in an acceptable amount of time, and there is no stuttering.

But we also have a separate application that sends a thumbnail to the main application (with a sound player) via TCP every second. The thumbnail is about 1300 bytes when encoded in JPEG format.

This is the code we use to decode the image:

MemoryStream ms = new MemoryStream(data); BitmapDecoder bdec = BitmapDecoder.Create(ms, BitmapCreateOptions.None, BitmapCacheOption.Default); BitmapSource source = bdec.Frames[0]; imgPreview.Source = source; 

And for coding:

 JpegBitmapEncoder jpgEncoder = new JpegBitmapEncoder(); jpgEncoder.QualityLevel = quality; jpgEncoder.Frames.Add(BitmapFrame.Create(renderTarget)); byte[] imageArray; using (MemoryStream outputStream = new MemoryStream()) { jpgEncoder.Save(outputStream); imageArray = outputStream.ToArray(); } 

Where RenderTarget is a RenderTargetBitmap that has image content.

Now we create and discard MemoryStream, BitmapDecoder and BitmapSource every second. I commented out the lines from the code, and it looks like the MemoryStream and the BitmapDecoder constructor do not stutter, but after it is accessed through Frames [0] it starts to stutter.

We also tried this approach instead of BitmapDecoder, but with the same results:

 img.BeginInit(); img.StreamSource = ms; img.EndInit(); 

Of course, is there a better way to keep the image up to date?

The best way is to simply send the raw image data and create a WriteableBitmap that is simply overwritten every second. But the original image is 170 kb, 100 times larger than the encoded image, and we really do not want to do this. Can I decode a JPEG stream into an existing byte array or an existing image?

+4
source share
2 answers

Ok, so I found a solution to my problem.

Instead of using WPF BitmapDecoder to decode images, I use Bitmap Windows Forms. It is disposable and much nicer for the Garbage Harvester.

So the solution is this:

 var stream = new MemoryStream(data); var formsBitmap = new Bitmap(stream); var width = formsBitmap.Width; var height = formsBitmap.Height; if (bitmap == null || height != bitmap.PixelHeight || width != bitmap.PixelWidth) { bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Pbgra32, null); imgPreview.Source = bitmap; } BitmapData data = formsBitmap.LockBits(new Rectangle(0, 0, formsBitmap.Width, formsBitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb); try { bitmap.WritePixels(new Int32Rect(0, 0, width, height), data.Scan0, data.Stride * data.Height, data.Stride); } finally { formsBitmap.UnlockBits(data); } formsBitmap.Dispose(); 

Instead of decoding every resulting JPEG frame, I create a new Bitmap (from WinForms) from the data. Then I just copy the pixels only to the WriteableBitmap that I use.

Hope this can help others too.

+4
source

Code profiling confirms that bdec.Frames [0]; takes up a relatively large part of the processor time Looking at the code in ILSpy, the " Frames " getter has an empty implementation (the virtual method is not overridden by the JpgBitmapDecodersub class), so I assume there is some kind of call made for the base Windows API there (?)

The bottom line is that decoding JPG will be slower than PNG or GIF; I would try PNG encoding, as it should still provide a good compression ratio, but with better performance.

+1
source

Source: https://habr.com/ru/post/1482519/


All Articles