Silverlight: How to copy region from WriteableBitmap / BitmapImage? - silverlight

I have such code for Bitmap wrapper.
I need to create overload constructor which cut some rectangle from source image and put it inside _wbmp.
Smth similar to public Bitmap(string fileName, Rectangle area).
Please share some solution.
public Bitmap(string fileName)
{
Uri uri = new Uri(fileName, UriKind.RelativeOrAbsolute);
StreamResourceInfo sri = null;
sri = Application.GetResourceStream(uri);
// Create a new WriteableBitmap object and set it to the JPEG stream.
BitmapImage bitmap = new BitmapImage();
bitmap.CreateOptions = BitmapCreateOptions.None;
bitmap.SetSource(sri.Stream);
_wbmp = new WriteableBitmap(bitmap);
}
Thank you

The WritableBitmap object has a Render method that you can use to render a new bitmap after adding some transformation. In your case you could create and new WritableBitmap with the correct new size to set the button-right corner and then add temporary image with your source and translate it to the left to set the upper left corner. Something like this:
public static WriteableBitmap CropBitmap(string fileName, int newTop, int newRight, int newBottom, int newLeft)
{
Uri uri = new Uri(fileName, UriKind.RelativeOrAbsolute);
StreamResourceInfo sri = null;
sri = Application.GetResourceStream(uri);
// Create a new WriteableBitmap object and set it to the JPEG stream.
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.CreateOptions = BitmapCreateOptions.None;
bitmapImage.SetSource(sri.Stream);
//calculate bounding box
int originalWidth = bitmapImage.PixelWidth;
int originalHeight = bitmapImage.PixelHeight;
int newSmallWidth = newRight - newLeft;
int newSmallHeight = newBottom - newTop;
//generate temporary control to render image
Image temporaryImage = new Image { Source = bitmapImage, Width = originalWidth, Height = originalHeight };
//create writeablebitmap
WriteableBitmap wb = new WriteableBitmap(newSmallWidth, newSmallHeight);
wb.Render(temporaryImage, new TranslateTransform { X = -newLeft, Y = -newTop });
wb.Invalidate();
return wb;
}

Related

With HelixToolkit.SharpDX.Wpf how do I set the DiffuseMap on a PhongMaterial from an ImageSource?

The DiffuseMap property of a PhongMaterial accepts a Stream.
If I have an ImageSource, how do I convert it to something acceptable to the property? Note that I need to be able to do this fast, in memory.
In the examples in the source code I can only find examples of loading images from file:
var image = LoadFileToMemory(new System.Uri(#"test.png", System.UriKind.RelativeOrAbsolute).ToString());
this.ModelMaterial = new PhongMaterial
{
AmbientColor = Colors.Gray.ToColor4(),
DiffuseColor = Colors.White.ToColor4(),
SpecularColor = Colors.White.ToColor4(),
SpecularShininess = 100f,
DiffuseAlphaMap = image,
DiffuseMap = LoadFileToMemory(new System.Uri(#"TextureCheckerboard2.dds", System.UriKind.RelativeOrAbsolute).ToString()),
NormalMap = LoadFileToMemory(new System.Uri(#"TextureCheckerboard2_dot3.dds", System.UriKind.RelativeOrAbsolute).ToString()),
};
LoadFileToMemory simply takes the bytes from a file and returns it as a MemoryStream.
By ImageSource you mean a BitmapSource or DrawingImage? ImageSource is the abstract base class for both of them.
If you have a BitmapSource you can convert it to a MemoryStream using:
private Stream BitmapSourceToStream(BitmapSource writeBmp)
{
Stream stream = new MemoryStream();
//BitmapEncoder enc = new PngBitmapEncoder();
//BitmapEncoder enc = new JpegBitmapEncoder();
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(writeBmp));
enc.Save(stream);
return stream;
}

How to convert System.Windows.Media.DrawingImage into Stream?

I'am trying convert DrawingImage into MemoryStream. My code looks like this:
public MemoryStream ImageStream(DrawingImage drawingImage)
{
MemoryStream stream = new MemoryStream();
ImageSource imageSource = drawingImage;
if (imageSource != null)
{
BitmapSource bitmap = imageSource as BitmapSource;
if (bitmap != null)
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bitmap));
encoder.Save(stream);
}
}
return stream;
}
But problem is after casting ImageSource into BitmapSource bitmap is always null. Any sugestion how to fix that?
The reason your bitmap variable is always null is because DrawingImage does not extend BitmapImage or vice-versa, so the cast is guaranteed to fail. A DrawingImage does not contain any pixel data of any kind. It references a Drawing that is used whenever the image needs to be rasterized.
How did you find yourself in a situation where you want to rasterize a DrawingImage and serialize it into a stream? I get the feeling you are going about something in an unusual way if you have need of a function like this.
Nevertheless, you could implement this function by drawing the DrawingImage to a DrawingVisual, rendering it to a RenderTargetBitmap, and then passing the render target to the encoder to serialize the raster data to a stream.
public MemoryStream ImageStream(DrawingImage drawingImage)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawDrawing(drawingImage.Drawing);
dc.Close();
}
RenderTargetBitmap target = new RenderTargetBitmap((int)visual.Drawing.Bounds.Right, (int)visual.Drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
target.Render(visual);
MemoryStream stream = new MemoryStream();
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(target));
encoder.Save(stream);
return stream;
}
If you want something a little more generic, I would split this into two methods and change some of the types.
public BitmapSource Rasterize(Drawing drawing)
{
DrawingVisual visual = new DrawingVisual();
using (DrawingContext dc = visual.RenderOpen())
{
dc.DrawDrawing(drawing);
dc.Close();
}
RenderTargetBitmap target = new RenderTargetBitmap((int)drawing.Bounds.Right, (int)drawing.Bounds.Bottom, 96.0, 96.0, PixelFormats.Pbgra32);
target.Render(visual);
return target;
}
public void SavePng(BitmapSource source, Stream target)
{
BitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(source));
encoder.Save(target);
}
Then you could use it with any kind of stream. For example, to save the drawing to a file:
using (FileStream file = File.Create("somepath.png"))
{
SavePng(Rasterize(drawingImage.Drawing), file);
}

How can I capture an entire image from a canvas?

I am working on a project to number features on an image. Each item being numbered is a textblock object being printed on top of a canvas that has a jpg(or bmp/gif/png) drawn to it's background using an image brush.
When it comes time to save the graphic I want to have two options, one to save it as a new jpg (or other format) that has the textblock objects rendered into it. or option b which would be to save the placements of those textblocks to a seperate file (XML, XAML what have you) so you could come back and continue to edit the entries.
Here is some of my code.
This is where I am trying to save the image
private void miSaveImage_Click(object sender, RoutedEventArgs e)
{
ImageBrush bi = (ImageBrush)canvas.Background;
Rect bounds = VisualTreeHelper.GetDescendantBounds(canvas);
double pw = (bi.ImageSource as BitmapSource).PixelWidth;
double ph = (bi.ImageSource as BitmapSource).PixelHeight;
double px = (bi.ImageSource as BitmapSource).DpiX;
double py = (bi.ImageSource as BitmapSource).DpiY;
//Get the actual width and height of the image, then the dpi and render an image of it
RenderTargetBitmap rtb = new RenderTargetBitmap((int)pw, (int)ph, px*96, py*96, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//create the encoder and save the image data to it.
BitmapEncoder pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
try
{
//eventually I need to make this a save dialog but that will come later.
System.IO.MemoryStream ms = new System.IO.MemoryStream();
pngEncoder.Save(ms);
ms.Close();
System.IO.File.WriteAllBytes("Sample.png", ms.ToArray());
}
catch (Exception err)
{
MessageBox.Show(err.ToString(), "Problem saving image", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
This is the code I use to load the image
//Global members
Image i;
static Size _Size = new Size();
private void miLoadImage_Click(object sender, RoutedEventArgs e)
{
// Create OpenFileDialog
Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
// Set filter for file extension and default file extension
dlg.DefaultExt = ".png";
dlg.Filter = #"All supported graphics|*.jpg;*.jpeg;*.png;*.gif;*.bmp|
JPEG Files (*.jpeg)|*.jpeg|
PNG Files (*.png)|*.png|
JPG Files (*.jpg)|*.jpg|
GIF Files (*.gif)|*.gif|
BMP Files (*.bmp)|*.bmp";
// Display OpenFileDialog by calling ShowDialog method
Nullable<bool> result = dlg.ShowDialog();
// Get the selected file name and display in a TextBox
if (result == true)
{
// Open document
string filename = dlg.FileName;
Uri uri = new Uri(#dlg.FileName, UriKind.Relative);
//Use canvas rather than image
ImageBrush ib = new ImageBrush();
ib.ImageSource = new BitmapImage(new Uri(filename, UriKind.Relative));
canvas.Background = ib;
if (i == null)
i = new Image();
i.Source = new BitmapImage(new Uri(dlg.FileName));
_Size = new Size(i.Source.Width, i.Source.Height);
}
}
Here is the code I use to write the text to the image.
public void WriteTextToImage(Point position)
{
SolidColorBrush brush = new SolidColorBrush((Color)cpColor.SelectedColor);
//Get something to write on (not an old receipt...)
TextBlock textBlock = new TextBlock();
//Say something useful... well something atleast...
textBlock.Text = tbCurrentLabel.Text;
textBlock.FontSize = slFontSize.Value;
textBlock.Foreground = brush;
Canvas.SetLeft(textBlock, position.X);
Canvas.SetTop(textBlock, position.Y);
canvas.Children.Add(textBlock);
//Need to update the canvas, was not seeing children before doing this.
canvas.UpdateLayout();
//Iterate the label text
IterateLabel();
}
Right now I am only getting a corner of the image that is currently visible in the canvas, and that is being cropped down, by fiddling with the DPI and _Size values I can change how much of the image is shown, but it is static to that one image (If I load a different image all the values are wrong and again I only get a small portion of the image)
If someone could please help me pull my head out of (ahem) I would be most grateful!
TIA
Save entire Canvas
RenderTargetBitmap bmp = new RenderTargetBitmap((int)Cnv.ActualWidth, (int)Cnv.ActualHeight, 100.0, 100.0, PixelFormats.Default);
bmp.Render(Cnv);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(BitmapFrame.Create(bmp));
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();
Save only Background
BitmapFrame bmp = (BitmapFrame)(Cnv.Background as ImageBrush).ImageSource;
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(bmp);
System.IO.FileStream stream = System.IO.File.Create("G:\\Canvas.png");
encoder.Save(stream);
stream.Close();

How can I convert byte[] to BitmapImage?

I have a byte[] that represents the raw data of an image. I would like to convert it to a BitmapImage.
I tried several examples I found but I kept getting the following exception
"No imaging component suitable to complete this operation was found."
I think it is because my byte[] does not actually represent an Image but only the raw bits.
so my question is as mentioned above is how to convert a byte[] of raw bits to a BitmapImage.
The code below does not create a BitmapSource from a raw pixel buffer, as asked in the question.
But in case you want to create a BitmapImage from an encoded frame like a PNG or a JPEG, you would do it like this:
public static BitmapImage LoadFromBytes(byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
var image = new BitmapImage();
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = stream;
image.EndInit();
return image;
}
}
When your byte array contains a bitmap's raw pixel data, you may create a BitmapSource (which is the base class of BitmapImage) by the static method BitmapSource.Create.
However, you need to specify a few parameters of the bitmap. You must know in advance the width and height and also the PixelFormat of the buffer.
byte[] buffer = ...;
var width = 100; // for example
var height = 100; // for example
var dpiX = 96d;
var dpiY = 96d;
var pixelFormat = PixelFormats.Pbgra32; // for example
var stride = (width * pixelFormat.BitsPerPixel + 7) / 8;
var bitmap = BitmapSource.Create(width, height, dpiX, dpiY,
pixelFormat, null, buffer, stride);
I ran across this same error, but it was because my array was not getting filled with the actual data. I had an array of bytes that was equal to the length it was supposed to be, but the values were all still 0 - they had not been written to!
My particular issue - and I suspect for others that arrive at this question, as well - was because of the OracleBlob parameter. I didn't think I needed it, and thought I could just do something like:
DataSet ds = new DataSet();
OracleCommand cmd = new OracleCommand(strQuery, conn);
OracleDataAdapter oraAdpt = new OracleDataAdapter(cmd);
oraAdpt.Fill(ds)
if (ds.Tables[0].Rows.Count > 0)
{
byte[] myArray = (bytes)ds.Tables[0]["MY_BLOB_COLUMN"];
}
How wrong I was! To actually get the real bytes in that blob, I needed to actually read that result into an OracleBlob object. Instead of filling a dataset/datatable, I did this:
OracleBlob oBlob = null;
byte[] myArray = null;
OracleCommand cmd = new OracleCommand(strQuery, conn);
OracleDataReader result = cmd.ExecuteReader();
result.Read();
if (result.HasRows)
{
oBlob = result.GetOracleBlob(0);
myArray = new byte[oBlob.Length];
oBlob.Read(array, 0, Convert.ToInt32(myArray.Length));
oBlob.Erase();
oBlob.Close();
oBlob.Dispose();
}
Then, I could take myArray and do this:
if (myArray != null)
{
if (myArray.Length > 0)
{
MyImage.Source = LoadBitmapFromBytes(myArray);
}
}
And my revised LoadBitmapFromBytes function from the other answer:
public static BitmapImage LoadBitmapFromBytes(byte[] bytes)
{
var image = new BitmapImage();
using (var stream = new MemoryStream(bytes))
{
stream.Seek(0, SeekOrigin.Begin);
image.BeginInit();
image.StreamSource = stream;
image.CreateOptions = BitmapCreateOptions.PreservePixelFormat;
image.CacheOption = BitmapCacheOption.OnLoad;
image.UriSource = null;
image.EndInit();
}
return image;
}
Create a MemoryStream from the raw bytes and pass that into your Bitmap constructor.
Like this:
MemoryStream stream = new MemoryStream(bytes);
Bitmap image = new Bitmap(stream);

Save Controls.Image to PDF with more than 1 Control.Image

I'm here for a solution to a little unresolved problem. Maybe I haven't searched for the right things as I haven't found a solution and I hope you will be able to help.
So I have a WPF application where I load an image, here I have no problems. Next I have to add some points by clicking, so i have this function:
private void ClickMouse(object sender, MouseButtonEventArgs e)
{
Point p = e.GetPosition(this);
schema.PointFromScreen(p);
var myImage3 = new Image();
var bi3 = new BitmapImage();
bi3.BeginInit();
bi3.UriSource = new Uri(_clickImagePath, UriKind.Relative);
bi3.EndInit();
myImage3.Stretch = Stretch.Fill;
myImage3.Source = bi3;
myImage3.MinWidth = 30;
myImage3.MinHeight = 30;
myImage3.Width = 30;
myImage3.Height = 30;
myImage3.Name = _pointName;
var oMargin = new Thickness(p.X, p.Y, 0, 0);
myImage3.Margin = oMargin;
mainCanva.Children.Add(myImage3);
_listImg.Add(myImage3);
PointList.Items.Add(_pointName);
}
The problem is not here but I need a context.
Now on to the main problem. I have to save the final Image to make a PDF with. I use a piece of sample code I found here (http://stackoverflow.com/questions/1824989/how-to-store-system-windows-controls-image-to-local-disk) to convert to a jpg file using ITextsharp. The only thing I have on the PDF is the first picture, so I admitted that it probably take only the first image and not the other created images on it but I really don't know how to fix it. Does some fonction exist to make a kind of screenshot of the image zone? or maybe to pay attention to all image in the zone of the principal Control.Image ?
Thank you for your help.
You should use the following code to render your Canvas as an image :
var rect = new Rect(canvas.RenderSize);
var rtb = new RenderTargetBitmap((int)rect.Right, (int)rect.Bottom, 96d, 96d, System.Windows.Media.PixelFormats.Default);
rtb.Render(canvas);
//encode as PNG
varpngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(rtb));
//save to memory stream
using(var ms = new System.IO.MemoryStream())
{
pngEncoder.Save(ms);
System.IO.File.WriteAllBytes("logo.png", ms.ToArray());
}
(Source : Write WPF output to image file )

Resources