I thought the answer might be also useful for others. Furthermore I will provide ready to use code for JPEG encoding and decoding of the WriteableBitmap.
Byte array conversion
Copy WriteableBitmap to ARGB byte array
public static byte[] ToByteArray(this WriteableBitmap bmp) { int[] p = bmp.Pixels; int len = p.Length * 4; byte[] result = new byte[len]; // ARGB Buffer.BlockCopy(p, 0, result, 0, len); return result; }
Copy ARGB byte array into WriteableBitmap
public static void FromByteArray(this WriteableBitmap bmp, byte[] buffer) { Buffer.BlockCopy(buffer, 0, bmp.Pixels, 0, buffer.Length); }
Usage
// Render UIElement into WriteableBitmap WriteableBitmap bmp = new WriteableBitmap(UIElement, null); // Copy WriteableBitmap.Pixels into byte array (format ARGB) byte[] buffer = bmp.ToByteArray(); // Create a new WriteableBitmap with the size of the original image WriteableBitmap bmp = new WriteableBitmap(width, height); // Fill WriteableBitmap from byte array (format ARGB) bmp.FromByteArray(buffer);
I will include the two methods in my WriteableBitmap extensions that I'm going to put up on Codeplex soon.
JPEG encoding and decoding
If you want to store many images or transport them over a network the needed storage size could quickly become a big problem. For example an image with the size 512 x 512 needs 1 Megabyte storage space and a 1024 x 768 image even 3 MB. A solution could be image compression using JPEG encoding and decoding. To accomplish this I've used the open source FJCore JPEG library which is distributed under the MIT License and works nicely with Silverlight.
Encode WriteableBitmap as JPEG stream
public static void EncodeJpeg(WriteableBitmap bmp, Stream destinationStream) { // Init buffer in FluxJpeg format int w = bmp.PixelWidth; int h = bmp.PixelHeight; int[] p = bmp.Pixels; byte[][,] pixelsForJpeg = new byte[3][,]; // RGB colors pixelsForJpeg[0] = new byte[w, h]; pixelsForJpeg[1] = new byte[w, h]; pixelsForJpeg[2] = new byte[w, h]; // Copy WriteableBitmap data into buffer for FluxJpeg int i = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int color = p[i++]; pixelsForJpeg[0][x, y] = (byte)(color >> 16); // R pixelsForJpeg[1][x, y] = (byte)(color >> 8); // G pixelsForJpeg[2][x, y] = (byte)(color); // B } } // Encode Image as JPEG using the FluxJpeg library // and write to destination stream ColorModel cm = new ColorModel { colorspace = ColorSpace.RGB }; FluxJpeg.Core.Image jpegImage = new FluxJpeg.Core.Image(cm, pixelsForJpeg); JpegEncoder encoder = new JpegEncoder(jpegImage, 95, destinationStream); encoder.Encode(); }
Decode WriteableBitmap from JPEG stream
public static WriteableBitmap DecodeJpeg(Stream sourceStream) { // Decode JPEG from stream var decoder = new FluxJpeg.Core.Decoder.JpegDecoder(sourceStream); var jpegDecoded = decoder.Decode(); var img = jpegDecoded.Image; img.ChangeColorSpace(ColorSpace.RGB); // Init Buffer int w = img.Width; int h = img.Height; var result = new WriteableBitmap(w, h); int[] p = result.Pixels; byte[][,] pixelsFromJpeg = img.Raster; // Copy FluxJpeg buffer into WriteableBitmap int i = 0; for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { p[i++] = (0xFF << 24) // A | (pixelsFromJpeg[0][x, y] << 16) // R | (pixelsFromJpeg[1][x, y] << 8) // G | pixelsFromJpeg[2][x, y]; // B } } return result; }
Usage
// Save rendered UIElement as JPEG file private void BtnSaveFile_Click(object sender, RoutedEventArgs e) { if (saveFileDlg.ShowDialog().Value) { using (Stream dstStream = saveFileDlg.OpenFile()) { // Render to WriteableBitmap WriteableBitmap bmp = new WriteableBitmap(UIElement, null); // Encode JPEG and write to FileStream EncodeJpeg(bmp, dstStream); } } } // Open JPEG file and read into a WriteableBitmap private void BtnLoadFile_Click(object sender, RoutedEventArgs e) { if (openFileDialog.ShowDialog().Value) { using (System.IO.Stream srcStream = openFileDialog.File.OpenRead()) { // Read JPEG file and decode it WriteableBitmap bmp = DecodeJpeg(srcStream); } } }
Keep in mind that the standard JPEG format doesn't support alpha values (transparency) and that the compression is lossy. So don't encode and decode images subsequently with JPEG.
It is also possible to use the built-in Silverlight class BitmapSource and its SetSource method to decode an JPEG stream.
public static WriteableBitmap DecodeJpegWithBitmapSource(Stream sourceStream) { // Decode JPEG from stream var bitmapSource = new BitmapSource(); bitmapSource.SetSource(sourceStream); return new WriteableBitmap(bitmapSource); }
Source code
Check out my Codeplex project WriteableBitmapEx for an up to date version of the byte array conversion methods.
Hi Rene!
ReplyDeletehere's a bit optimized ToByteArray():
public static byte[] ToByteArray(this WriteableBitmap bmp)
{
int[] p = bmp.Pixels;
int len = p.Length << 2;
byte[] result = new byte[len];
Buffer.BlockCopy(p, 0, result, 0, len);
return result;
}
FromByteArray() can be done in a similar fashion. I found this nice function today :)
I'd add it to the WriteableBitmapEx on codeplex, but have too much other stuff to do :)
Thanks for the suggestion Nikola. I recently added a fastpath to the Blitting function using Buffer.BlockCopy(), which is 3x faster even for copying of each scanline. Somehow I forgot to alter the Convert methods. I'll change it. Thanks again for the info.
ReplyDeleteBTW, I left a comment on your blog post about the TGA save, but you haven't replied yet.
Yes, I saw the comment just now. I really have to figure out a way to manage comments better - my site hoster's email service doesn't work as expected with the .NETblogengine and also having some spamming problems.. :) Nice job on the blitting :)
ReplyDeletebtw, I wonder which is faster: Array.Copy() or Buffer.BlockCopy()
I just checked the implementation of Array.Copy and Buffer.BlockCopy using Reflector. Both map to internal CLR methods. So I guess the difference is not that much.
ReplyDeleteTHanks for the article. Very interesting indeed. I am interested in a screen sharing application with SL and I wonder if your code can be used for this purpose. Any comment is highly appreciated. Thanks
ReplyDeleteHi,
ReplyDeleteit could work using MJPEG compression, but the biggest problem is the latency and the sync. See the latest discussion here:
http://forums.silverlight.net/forums/t/145729.aspx
And you need to make an elevated trust app in order to use COM for screen capturing.
Riu made a nice OOB chat application including screen capturing, without compression. Check out the blog post and the video:
http://pontonetpt.com/blogs/ruimarinho/archive/2010/02/16/streaming-webcam-amp-desktop-em-silverlight.aspx
Hello,
ReplyDeleteMaybe I just don't see it, but how could I use the FromByteArray() Function without knowing the ImageSize?
I would do something like this:
- Open a File with the OpenFileDialog
- Get the ByteArray from the Stream
- Use FromByteArray to Fill the WritableBitmap
- Resize it
- And save it to the DB with ToByteArray()
But to use the FromByteArray Function I have to create a WritableBitmap with a Size which I don't know...???
Any help would be great!
Thanks, Markus
That's true, you have also to store the size of the image in the DB, since you need to know the width and the height to create a WriteableBitmap.
ReplyDeleteUse this code to load an image from disk:
OpenFileDialog openFileDlg = new OpenFileDialog();
if (openFileDlg.ShowDialog().Value)
{
using (var stream = openFileDlg.File.OpenRead())
{
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
var wb = new WriteableBitmap(bitmapImage);
byte[] data = wb.ToByteArray();
// ... Write data and wb.PixelWidth + wb.PixelHeight to the DB
}
}
...
Load the image from the DB:
// ... Get data and width + height from DB
var wb = new WriteableBitmap(width, height);
wb.FromByteArray(data);
Hello Rene,
ReplyDeletethanks a lot for your very quick relpy!!! Now it works for me.
In addition I use the FJCore Library to convert the WritableBitmap to a Jepg using your function from above before saving it into the DB so I don't have to remember the Width and Height... here is the Code...
private void DoExecuteUploadDocumentPreviewCommand() {
var dlg = new OpenFileDialog();
dlg.Multiselect = false;
dlg.Filter = "Vorschau Bilder|*.jpg;*.jpeg;*.png;*.bmp";
if ((bool)dlg.ShowDialog()) {
var file = dlg.File;
if (file != null) {
using (var stream = file.OpenRead()) {
_dokument.Thumbnail = EnshureThumbSize(stream);
}
//Remember Original FileName
_dokument.ThumbnailOriginalFileName = file.Name;
try {
//Works only in FullTrust Mode, but we try it... (If not in full Trust Mode, we remember only the Name of the File)
_dokument.ThumbnailOriginalFileName = file.FullName;
} catch { }
SubmitChanges();
InvokePropertyChanged("DocumentThumbImageSource");
}
}
}
private byte[] EnshureThumbSize(FileStream stream) {
//Read the Stream into a BitmapImage
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
//Resize the Image - keeping the aspect ratio
var orgImage = new WriteableBitmap(bitmapImage);
var targetSize = GetScaleSize(new Size(orgImage.PixelWidth, orgImage.PixelHeight), new Size(120, 120));
var thumbImage = orgImage.Resize(Convert.ToInt32(targetSize.Width), Convert.ToInt32(targetSize.Height), WriteableBitmapExtensions.Interpolation.Bilinear);
//Convert it into a jpg with FJCore
var destStream = new MemoryStream();
thumbImage.EncodeJpeg(destStream);
//Return a byte[] - to be saved in DB (so you don't have to remember with and height)
destStream.Position = 0;
var buffer = new byte[destStream.Length];
destStream.Read(buffer, 0, buffer.Length);
return buffer;
}
private Size GetScaleSize(Size orgSize, Size maxSize) {
var retSize = new Size(orgSize.Width, orgSize.Height);
if (orgSize.Width > maxSize.Width || orgSize.Height > maxSize.Height) {
double factorX = 1;
double factorY = 1;
if (orgSize.Width > maxSize.Width) factorX = orgSize.Width / maxSize.Width;
if (orgSize.Height > maxSize.Height) factorY = orgSize.Height / maxSize.Height;
double factor = 1;
if (factorX > factorY) factor = factorX; else factor = factorY;
retSize.Width = orgSize.Width / factor;
retSize.Height = orgSize.Height / factor;
}
return retSize;
}
nice Greetings,
Markus
Great. Converting the image to a JPEG is also better for the disk space. :-)
ReplyDeleteGlad I could help you.
Can you give an example of using IValueConverter interface to implement two way binding in an Image control. I tried this for a long time but with now success. It will be a lot of help if you can do this.
ReplyDeleteThanks for this great article.
I guess you want to bind a WriteableBitmap to an Image control's Source property.
ReplyDeleteThe problem is, in XAML the Source value of the image control is interpreted as URI.
"You can set this property in XAML, but in this case you are setting the property as a URI. The XAML behavior relies on underlying type conversion that processes the string as a URI, and calls the BitmapImage(Uri) constructor. This in turn potentially requests a stream from that URI and returns the image source object."
- taken from http://msdn.microsoft.com/en-us/library/system.windows.controls.image.source(v=VS.95).aspx
So the Image control is always calling the BitmapImage(Uri) constructor internally.
I don't like it either, but it's easier done in code-behind using this snippet:
myImageControl.Source = myWriteableBitmap;
Why would creating a WriteableBitmap on the UI thread using a memory stream give a Catastrophic failure? I get an Exception E_UNEXPECTED when calling WriteableBitmap.SetSource using a new MemoryStream created from a byte[]. I'm trying to compress the sample from the VideoSink using FJCore but it throws when I set the source:
ReplyDeleteprotected override void OnSample(
long sampleTime,
long frameDuration,
byte[] sampleData)
{
...
System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
{
WriteableBitmap bitmap = new WriteableBitmap(w, l);
MemoryStream memStrm = new MemoryStream(sampleData);
bitmap.SetSource(memStrm); // Throws here
CompressData(bitmap);
}
});
...
}
I’ve even tried creating the bitmap from a stream that used a static byte array:
byte[] testData = new byte[] { 0X42, 0X4D, ... 0XFF, 0X00 };
and it still throws. What’s weird, creating the bitmap using the static array from the button’s event handler works fine. But creating it using the handler from the Dispatcher (even though they execute on the same thread) will cause the exception.
What am I missing here?
Hi OurTie,
ReplyDeleteI'd recommend to use this Dispatcher:
Application.Current.RootVisual.Dispatcher
I guess this won't change much. But why do you want to use a memory stream? You get the pixels as interleaved ARGB byte array and want to fill a WriteableBitmap that uses an ARGB int as pixel.
You can do something like this:
// Copy sample data to WriteableBitmap
var h = vidFormat.PixelHeight;
var s = vidFormat.Stride;
var bitmap = new WriteableBitmap(w, h);
var pixels = bitmap.Pixels;
int offset, sp;
if (s < 0)
{
for (int y = 0; y < h; y++)
{
sp = -s;
offset = sp * (h - (y + 1));
Buffer.BlockCopy(sampleData, offset, pixels, sp * y, sp);
}
}
else
{
for (int y = 0; y < h; y++)
{
offset = s * y;
Buffer.BlockCopy(sampleData, offset, pixels, offset, s);
}
}
Hi,
ReplyDeleteI am trying to resized dropped file and upload it on the server. Here is my code:
private void DisplayPanel_Drop(object sender, DragEventArgs e)
{
StackPanel sp = sender as StackPanel;
object dropObjectArray = e.Data.GetData("FileDrop");
FileInfo[] fileObj = dropObjectArray as FileInfo[];
foreach (FileInfo fi in fileObj)
{
FileStream dropFile = fi.OpenRead();
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(dropFile);
WriteableBitmap wb = new WriteableBitmap(bitmapImage);
WriteableBitmap resizedWb = wb.Resize(100, 100, WriteableBitmapExtensions.Interpolation.Bilinear);
byte[] data = resizedWb.ToByteArray();
Image image = new Image();
image.Source = resizedWb;
sp.Children.Add(image);
// upload file resized file on the server
UploadFile(data,dropFile.Name);
}
}
// I am uploading the file using DomainService
File.WriteAllBytes(fileName, data);
The resized image is showing up inside stackpanel, but the saved file in the server seems not in a correct format. Windows cannot read the file and says this is not a valid bitmap file.
What is wrong?
Hi Seeed,
ReplyDeleteyou're just converting it to an ARGB byte array which is then saved. This raw data is not a common file format for windows.
Try the WriteableBitmapEx WriteTga() method instead or use the JPEG encoding I provided above.
Hi All,
ReplyDeleteI am new to Silverlight and using this to upload multiple Tif files but before uploading want to put condition on Width and Height. Below code is working fine for JPG but not for TIF.
Anyhelp would be much appreciated. Thanks in advance
//Read the Stream into a BitmapImage
var bitmapImage = new BitmapImage();
bitmapImage.SetSource(stream);
here I can retrived the pixelwidth and height using bimapImage.pixelwidth but not working for TIF.
Regards
Puran
TIFF is not supported. Only PNG and JPG images can be used.
ReplyDeleteSee http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage(VS.95).aspx
Is there any work-around to deal with TIFF files
ReplyDelete@sonu: http://lmgtfy.com/?q=silverlight+tiff
ReplyDeleteHi Rene,
ReplyDeleteIs there a way to convert a WriteableBitmap back to a BitmapImage?
I'm using a WriteableBitmap to create thumbnails of user selected images; the problem is the application is an upgraded Silverlight 2 app and it makes a lot of use of BitmapImages and would take too long to fully convert.
Thanks
The only would be to convert the WriteableBitmap data to a supported format like JPEG and use BitmapImage.SetSource with a MemoryStream.
ReplyDeleteI would strongly recommend to change the code so it uses the base class of the WriteableBitmap, BitmapImage, the BitmapSource. See:
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapsource(v=VS.95).aspx
Hi Rene, many thanks for the details; I have changed the code to use the base BitmapSource class and it allows me swap between the WriteableBitmap and BitmapImage classes when displaying local and remote images.
ReplyDeleteRene',
ReplyDeleteIm trying to verify that the byte[] I get back from the EncodeJpeg method is a valid jpeg image.
byte[] jpegImage;
using (MemoryStream dstStream = new MemoryStream())
{
// Encode JPEG and write to FileStream
Methods.EncodeJpeg(lastSnapShot, dstStream);
//Return a byte[] - to be saved in DB
dstStream.Position = 0;
var buffer = new byte[dstStream.Length];
dstStream.Read(buffer, 0, buffer.Length);
jpegImage = buffer;
MemoryStream ms = new MemoryStream();
ms.Read(jpegImage, 0, jpegImage.Length);
WriteableBitmap x = new WriteableBitmap(480, 640);
x = Methods.DecodeJpeg(ms);
this.image1.Source = x;
The FJcore class BinaryReader keeps erroring on me telling me:
"attempting to read past the end of the stream"
Any suggestions?
You've written to the stream, so it's at the end. Just use Seek or Position = 0 on the stream.
ReplyDeleteBrilliant!
ReplyDeleteIt is working, but any reason the image comes out looking all crazy?
So my test was this:
ReplyDelete// Without any encoding the captured image (the writeable bitmap) looks perfect
this.image1.Source = lastSnapShot;
// After the FJcore encoding the image is corrupt
using (MemoryStream dstStream = new MemoryStream())
{
// Encode JPEG and write to FileStream
Methods.EncodeJpeg(lastSnapShot, dstStream);
//Return a byte[] to be saved in DB
var buffer = new byte[dstStream.Length];
dstStream.Read(buffer, 0, buffer.Length);
// At this point dstStream should contain the encoded jpeg
// Next we will decode it and set it as the source of an image object to test it
dstStream.Position = 0;
WriteableBitmap x = new WriteableBitmap(640, 480);
x = Methods.DecodeJpeg(dstStream);
this.image1.Source = x;
}
// The image comes out like it's repeating the top 15% of the picture and has been rotated and is very skewed. It is un-viewable.
Any ideas?
Hi,
ReplyDeleteUsing the above code in Windows Phone 7 works for the encode, but I get the following exception in the decode: System.Reflection.Emit.OpCodes.Ldarg_0 on line var jpegDecoded = decoder.Decode();
I can't work out why. Any ideas?
Hi Anonymous,
ReplyDeleteReflection.Emit is not supported by the .Net Compact Framework, which is the underlying runtime on Windows Phone.
You should use LoadJpeg and SaveJpeg to decode / encode a WriteableBitmap load / save as JPEG stream.
http://msdn.microsoft.com/en-us/library/ff707350(v=VS.92).aspx
Thanks heaps. I knew there must have been an library for this, but I couldn't find it.
ReplyDeleteRegards Ray (not Anonymous)
Jim,
ReplyDeleteI have the same exact issue.
public class ImageByteConverter : IValueConverter
{
///
/// Converts a Jpeg byte array into a WriteableBitmap
///
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
byte[] val = (byte[])value;
MemoryStream ms = new MemoryStream(val);
return WriteableBitmapExtentions.DecodeJpeg(ms);
}
///
/// Converts a WriteableBitmap into a Jpeg byte array.
///
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null)
return null;
WriteableBitmap wb = (WriteableBitmap)value;
return wb.EncodeJpeg();
}
}
AHA!
ReplyDeleteYou accidentally reversed the interval of x and y... it should be :
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
p[i++] = (0xFF << 24) // A
| (pixelsFromJpeg[0][x, y] << 16) // R
| (pixelsFromJpeg[1][x, y] << 8) // G
| pixelsFromJpeg[2][x, y]; // B
}
}
Good catch David. Yes, the Decode is reversed. I must admit that I always use the built-in BitmapImage class and its SetSource method for JPEG decoding.
ReplyDeleteAnother thing about the IValueConverter. Don't do this subsequently with the same image. JPEG is a lossy compression and will result in image artifacts if the encode is performed too often. You should rather save the original stream if it is already encoded.
Rene,
ReplyDeleteI didn't think about the artifacts. THanks!
The ToByteArray method seems to be exactly what I need for my app. Unfortunately when I save the byte[] to a file, it's not creating a valid image. (Similarly if I upload the byte[] to a SharePoint document library, the image is invalid as well.)
ReplyDeleteI've validated that the WriteableBitmap is valid. The new file does consist of 153,600 bytes, which equals the height x width x 4, which I'm guessing is right. Any suggestions on what else I may be able to do to debug? I'm assuming the methods simply works "as is" and doesn't require and changes.
Buffer.BlockCopy(buffer, 0, bmp.Pixels, 0, buffer.Length);
ReplyDeleteThis will not work ( Phone 7 ) because bmp.Pixels is a readonly property and it can not be assigned.
Nope, the Pixels property has a setter.
ReplyDeleteHi Rene
ReplyDeleteLove your articles, thanks they help me a lot.
One point that I would like to request...
I know all developers have fantastic memory and know by instinct what name-space to use for all code, but... for us lesser developer types could you add the name-space
Many thanks
MrBronz
Hi Rene,
ReplyDeleteGreat article.
Could I still use this method to encode the video captured with the webcam?
Thanks in advance.
hello Rene,
ReplyDeleteI was working on sending a webcam-captured image to the server using WCF service & i was using this ToByteArray() method
//CLIENT Side
byte[] byteArray = writeableBitmapSource.ToByteArray();
ImageGenerateServiceClient serviceClient = new ImageGenerateServiceClient();
serviceClient.GenerateCompleted += new EventHandler(serviceClient_GenerateCompleted);
serviceClient.GenerateAsync(byteArray);
//SERVER Side
MemoryStream memoryStream = new MemoryStream(byteArray);
Image image = Image.FromStream(memoryStream);
...then I was using image.Save() method. But what i get is "Parameter is not valid." execption :(
...then for testing purpose i used the returned byte[] to provide the Source of an Image control as follows
MemoryStream memoryStream = new MemoryStream(byteArray);
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.SetSource(memoryStream);
imgPreview.Source = bitmapImage;
...but this time i got "Catastrophic failure (Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED))" exception :( :(
what did i miss? can you help?
You have to encode it on the client side to a JPEG, not just a byte array of the raw ARGB data. The Image.FromStream and BitmapImage expect a JPEG.
ReplyDeletethanks a LOT man, it's working. my first silverlight app & my first wcf service, and it's WORKIN' :D
ReplyDeleteHi Rene
ReplyDeleteYou say to Nero that he must encode it on the client side to a JPEG, not just a byte array of the raw ARGB .How can I do that I have the same problem.
Just read THiS blog post.
ReplyDeleteHey Illya,
ReplyDeleteYou can find all you need in the above blog post in the DecodeJpeg method. Just remove the part where the WriteableBitmap is created and the pixels are copied into it.
Hello Rene,
ReplyDeleteI tried to implement the conversion of the WriteableBitmap to an ARGB byte array that you posted but I can't seem to find the Pixels property. (int[] p = bmp.Pixels;)
Can you tell me what's the problem?
Thank you.
Hello!
ReplyDeleteCan I save an image to BMP format?
Sure you can, you just have to implement the BMP spec which should be rather easy. I guess there's an implementation available out there you can re-use. The WriteableBitmapEx also has a WriteTga extension method you can use. Tga is similar o bmp but supports the alpha channel.
ReplyDeleteHello Rene!
ReplyDeleteUsing EncodeJpeg method is it possible to calculate beforehand the second argument "quality" in JpegEncoder constructor? I mean if I have a max weight for final jpeg image and if while encoding it with Encode method of JpegEncoder I get the weight bigger than required I need to resize the image again and again lowing down the quality parameter. So are there ways to figure out the quality parameter?
Stanley
Hey Stanley,
ReplyDeleteI'm not aware there's something like this built-in. You can probably make your own heuristics, but in the end the best result is just computing the size in the background. See how pro photo editing software handles it.
please convert this part into vb.net
ReplyDeletepublic static void EncodeJpeg(this WriteableBitmap bitmap, MemoryStream stream, int quality = 90)
{
int width = bitmap.PixelWidth;
int height = bitmap.PixelHeight;
int bands = 3;
byte[][,] raster = new byte[bands][,];
for (int i = 0; i < bands; i++)
{
raster[i] = new byte[width, height];
}
for (int row = 0; row < height; row++)
{
for (int column = 0; column < width; column++)
{
int pixel = bitmap.Pixels[width * row + column];
raster[0][column, row] = (byte)(pixel >> 16); //R
raster[1][column, row] = (byte)(pixel >> 8); //G
raster[2][column, row] = (byte)pixel; //B
}
}
ColorModel model = new ColorModel { colorspace = ColorSpace.RGB };
FluxJpeg.Core.Image img = new FluxJpeg.Core.Image(model, raster);
JpegEncoder encoder = new JpegEncoder(img, quality, stream);
encoder.Encode();
stream.Seek(0, SeekOrigin.Begin);
}
C# <-> VB.Net
ReplyDeletehttp://converter.telerik.com