Getting BitmapImage PixelHeight Property causes Initialization Issues - c#

I have a BitmapImage that I want to get the PixelHeight and PixelWidth properties so I can determine whether it has a landscape or portrait layout. After I determine its layout, I need to set either the height or width of the image to get it to fit into my image viewer window without distorting the height:width ratio. However, it appears I have to call BeginInit() in order to do anything with my image. I have to call EndInit() to get the PixelHeight or PixelWidth properties and I cannot call BeginInit() more than once on the same BitmapImage object. So how can I possibly set my image, get the height and width, determine its orientation and then resize the image?
Here the chunk of code that I have been working with:
image.BeginInit();
Uri imagePath = new Uri(path + "\\" + die.Die.ImageID + "_" + blueTape + "_BF.BMP");
image.UriSource = imagePath;
//image.EndInit();
imageHeight = image.PixelHeight;
imageWidth = image.PixelWidth;
//image.BeginInit();
// If the image is taller than it is wide, limit the height of the image
// i.e. DML87s and all non-rotated AOI devices
if (imageHeight > imageWidth)
{
image.DecodePixelHeight = 357;
}
else
{
image.DecodePixelWidth = 475;
}
image.EndInit();
When I run this, I get this run-time exception:
InvalidOperationException:
BitmapImage initialization is not complete. Call the EndInit method to
complete the initialization.
Does anybody know how to deal with this issue?

As far as I know, what you want to do is not possible without decoding the bitmap twice.
I guess it would be a lot simpler to just decode the bitmap to its native size and then set the size of the containing Image control as needed. The bitmap is scaled appropriately, as Stretch is set to Uniform (since both width and height of the Image control are set, Stretch could as well be set to Fill or UniformToFill).
var bitmap = new BitmapImage(new Uri(...));
if (bitmap.Width > bitmap.Height)
{
image.Width = 475;
image.Height = image.Width * bitmap.Height / bitmap.Width;
}
else
{
image.Height = 475;
image.Width = image.Height * bitmap.Width / bitmap.Height;
}
image.Stretch = Stretch.Uniform;
image.Source = bitmap;

Related

WriteableBitmap.Pixels in Windows Phone 8.1

I am writing a Windows Phone 8.1 (WINRT) App. I am using fileopenpicker to choose a picture from gallery and then i display it in my app. But i want that user can crop this image before getting displayed on app.
In windows phone 8, we used Photochooser task having width property and cropping options automatically used to come.
Now I am trying to use this :
Windows Phone 8.0: Image Crop With Rectangle
But there is no WriteableBitmap.Pixels in Windows Phone 8.1. What to use instead of WriteableBitmap.Pixels?
// Create a new WriteableBitmap. The size of the bitmap is the size of the cropping rectangle
// drawn by the user, multiplied by the image size ratio.
WB_CroppedImage = new WriteableBitmap((int)(widthRatio * Math.Abs(Point2.X - Point1.X)), (int)(heightRatio * Math.Abs(Point2.Y - Point1.Y)));
// Calculate the offset of the cropped image. This is the distance, in pixels, to the top left corner
// of the cropping rectangle, multiplied by the image size ratio.
int xoffset = (int)(((Point1.X < Point2.X) ? Point1.X : Point2.X) * widthRatio);
int yoffset = (int)(((Point1.Y < Point2.Y) ? Point1.Y : Point2.X) * heightRatio);
// Copy the pixels from the targeted region of the source image into the target image,
// using the calculated offset
if (WB_CroppedImage.Pixels.Length > 0)
{
for (int i = 0; i < WB_CroppedImage.Pixels.Length; i++)
{
int x = (int)((i % WB_CroppedImage.PixelWidth) + xoffset);
int y = (int)((i / WB_CroppedImage.PixelWidth) + yoffset);
WB_CroppedImage.Pixels[i] = WB_CapturedImage.Pixels[y * WB_CapturedImage.PixelWidth + x];
}
// Set the source of the image control to the new cropped bitmap
FinalCroppedImage.Source = WB_CroppedImage;
}
else
{
FinalCroppedImage.Source = null;
}
You should take a look at BitmapEncoder and BitmapDecoder classes.
Also you probably will be able to use BitmapBounds to crop your image - set 'X' and 'Y' along with 'Width' and 'Height'.
I think the code may look like this (but I've not tested it):
StorageFile destination; // your destination file
using (var sourceStream = await sourceFile.OpenAsync(FileAccessMode.Read))
{
BitmapDecoder bmpDecoder = await BitmapDecoder.CreateAsync(sourceStream);
// here you scale your image if needed and crop by setting X, Y, Width and Height
BitmapTransform bmpTransform = new BitmapTransform() { ScaledHeight = scaledHeight, ScaledWidth = scaledWidth, InterpolationMode = BitmapInterpolationMode.Cubic, Bounds = new BitmapBounds { X = topLeftX, Y = topLeftY Width = desiredSizeW, Height = desiredSizeH } };
PixelDataProvider pixelData = await bmpDecoder.GetPixelDataAsync(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, bmpTransform, ExifOrientationMode.RespectExifOrientation, ColorManagementMode.DoNotColorManage);
using (var destFileStream = await destination.OpenAsync(FileAccessMode.ReadWrite))
{
BitmapEncoder bmpEncoder = await BitmapEncoder.CreateAsync(BitmapEncoder.PngEncoderId, destFileStream);
// here you need to set height and width - take from above
bmpEncoder.SetPixelData(BitmapPixelFormat.Rgba8, BitmapAlphaMode.Straight, desiredSizeW, desiredSizeH, 300, 300, pixelData.DetachPixelData());
await bmpEncoder.FlushAsync();
}
}
Of course you don't need to save the edited picture to StorageFile - I've used it as an example, you can write to stream and then set your image source.

RenderTargetBitmap getting error insufficient memory

I am getting insufficient memory exception when i am trying to render a image(10000 X 10000) resolution with canvas(10000 X 10000) resolution.
bmp = new RenderTargetBitmap((int)width, (int(height),graphics.DpiX,graphics.DpiY,PixelFormats.Default)bmp.Render(vs);
dc.PushTransform(new TranslateTransform(difX + left - horizontalScale, difY + top - verticalScale));
dc.PushTransform(new ScaleTransform(1, 1));
// Ask canvas to draw overlays
drawingCanvas.Draw(dc);
// Restore old actual scale.
drawingCanvas.ActualScale = oldActualScale;
// Restore clip
drawingCanvas.RefreshClip();
dc.Pop();
dc.Pop();
dc.Pop();
dc.Close();
width = (Utilityhelper.GetDIPIndependentHorizontal(rect.Width));
height = (Utilityhelper.GetDIPIndependentVertical(rect.Height));
bmp = new RenderTargetBitmap((int)width, (int)(height), Utilityhelper.graphics.DpiX, Utilityhelper.graphics.DpiY, PixelFormats.Default);
if (bmp != null)
{
bmp.Render(vs);
}
Done with using this.... DecodePixelWidth & DecodePixelHeight http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.bitmapimage.decodepixelwidth(v=vs.110).aspx

How to crop or resize Image to set aspect ratio in asp.net

I have an image control in my webform. I set its width 100px and height 100px. but if someone upload an image of ratio 100 * 120. I want it crop or resize and set 100 * 100. I tried to set max width but not worked , I tried bitmap method with code
protected void Button1_Click(object sender, EventArgs e)
{
if (FileUpload1.HasFile)
{
string filename = FileUpload1.FileName;
string directory = Server.MapPath("~/");
Bitmap originalBMP = new Bitmap(FileUpload1.FileContent);
float origWidth = originalBMP.Width;
float origHeight = originalBMP.Height;
float sngRatio = origWidth / origHeight;
float newWidth = 100;
float newHeight = newWidth / sngRatio;
Bitmap newBMP = new Bitmap(originalBMP,Convert.ToInt32(newWidth),Convert.ToInt32(newHeight));
Graphics oGraphics = Graphics.FromImage(newBMP);
oGraphics.SmoothingMode=System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
oGraphics.InterpolationMode=System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
oGraphics.DrawImage(originalBMP, 0, 0, newWidth, newHeight);
newBMP.Save(directory + filename);
originalBMP = null;
newBMP = null;
oGraphics = null;
Label1.Text = "File <b style='color: red;'>" + filename + "<</b> uploaded.";
Image1.ImageUrl = #"~/" + filename;
}
else
{
Label1.Text = "No file uploaded!";
}
}
it worked but it save the resized image in directery I want to save original image in directory and display the resize image in image control.
Check out Web Image Resize handler on codeplex : http://webimageresizer.codeplex.com/
It is a custom handler to process images.
Sample urls
Taken from the codeplex project's home page
// Returns the image mapping to /bla.jpg resized to width of 100 pixels preserving aspect relative to height
/ImageHandler.ashx?src=/bla.jpg&width=100
// Returns the image mapping to /bla.jpg resized to height of 100 pixels preserving aspect relative to width
/ImageHandler.ashx?src=/bla.jpg&height=100
// Returns the image mapping to /bla.jpg resized to width 100 pixels and a height of 50 pixels
/ImageHandler.ashx?src=/bla.jpg&width=100&height=50
Another option is to use http://imageresizing.net/
The benefit is that it registers a handler to handle images transparently, meaning, that you only add querystring variables to your original image url to manipulate the image.
Sample urls
/images/bla.jpg?h=100&w=100

RenderTargetBitmap renders image of a wrong size

My task is to show the user a thumbnail of each page of his XPS document. I need all the images to be small, so I render them with dpi set to 72.0 (I have googled that size of an A4 sheet with dpi 72.0 is 635x896). Basically, I do the following:
List<BitmapImage> thumbnails = new List<BitmapImage>();
documentPaginator.ComputePageCount();
int pageCount = documentPaginator.PageCount;
for (int i = 0; i < pageCount; i++)
{
DocumentPage documentPage = documentPaginator.GetPage(i);
bool isLandscape = documentPage.Size.Width > documentPage.Size.Height;
Visual pageVisual = documentPage.Visual;
//I want all the documents to be less or equals to A4
//private const double A4_SHEET_WIDTH = 635;
//private const double A4_SHEET_HEIGHT = 896;
//A4 sheet size in px, considering 72 dpi
RenderTargetBitmap targetBitmap = new RenderTargetBitmap(
(int)(System.Math.Min(documentPage.Size.Width, A4_SHEET_WIDTH)),
(int)(System.Math.Min(documentPage.Size.Height, A4_SHEET_HEIGHT)),
72.0, 72.0,
PixelFormats.Pbgra32);
targetBitmap.Render(pageVisual);
BitmapFrame frame = BitmapFrame.Create(targetBitmap);
PngBitmapEncoder encoder = new PngBitmapEncoder();
encoder.Frames.Add(frame);
BitmapImage image = new BitmapImage();
using (System.IO.MemoryStream ms = new System.IO.MemoryStream())
{
encoder.Save(ms);
image.BeginInit();
image.CacheOption = BitmapCacheOption.OnLoad;
image.StreamSource = ms;
if (isLandscape)
{
image.Rotation = Rotation.Rotate270;
}
image.EndInit();
}
thumbnails.Add(image);
}
But when I render a document page (A4) its size is actually 846x1194 instead of the one I expected. I tried to make the dpi lower (48.0) and the image's size has grown even bigger (I guess, I just do not quite undrestand what dpi is and how it affects the document). I tried to make dpi=96.0, and the size went smaller. I set one of images from the collection of instances of the class BitmapImage generated by the code above as source for the Image control (I am creating a WPF application) and in case dpi is set to 96.0 my program looks like this:
As you can see, part of the page is not shown at all, it does not fit inside the Image control, even though the size of the control is set to 635x896 that's why according to the code above, the image must be displayed correctly and all the text must fit.
What result do I expect in a nutshell: I am trying to create thumbnails of the pages of a document, but I want them to be smaller, relative to some number (I am sorry, I am not quite sure how do I say such stuff in English, basically if the document's page width is 1200 px I want it to be 1200/n, where n is the "some number" that I mentioned earlier), but if the shrinked image's size is still bigger than 635x896 I want the size to be 635x896.
Thanks in advance. And also I am sorry for my bad English.
First of all, DPI means Dots Per Inch, or pixel per inch. In the case of rendering an A4 page - which is 21 by 29.7 cm - to a bitmap at 72 DPI, you would end up with a bitmap of the following size:
Width = 21 cm / (2.54 cm/inch) * 72 pixel/inch = 595 pixel
Height = 29.7 cm / (2.54 cm/inch) * 72 pixel/inch = 842 pixel
Besides this you shouldn't care too much about DPI, with one exception: WPF rendering is done at 96 DPI. This means that an A4-sized page of your document is rendered to a 794 x 1123 bitmap. As a reminder:
Width = 21 cm / (2.54 cm/inch) * 96 pixel/inch = 794 pixel
Height = 29.7 cm / (2.54 cm/inch) * 96 pixel/inch = 1123 pixel
Hence, 794 x 1123 should be the size of your RenderTargetBitmap when it contains a page that is exactly A4. If the page size is less than A4, the bitmap should be smaller. If on the other hand the page is larger than A4, it should be scaled down to 794 x 1123. And that is the trick. Instead of rendering the page visual directly into the RenderTargetBitmap, you can put the visual into a ContainerVisual with a ScaleTransform as shown below.
for (int i = 0; i < paginator.PageCount; i++)
{
DocumentPage page = paginator.GetPage(i);
double width = page.Size.Width;
double height = page.Size.Height;
double maxWidth = Math.Round(21.0 / 2.54 * 96.0); // A4 width in pixels at 96 dpi
double maxHeight = Math.Round(29.7 / 2.54 * 96.0); // A4 height in pixels at 96 dpi
double scale = 1.0;
scale = Math.Min(scale, maxWidth / width);
scale = Math.Min(scale, maxHeight / height);
ContainerVisual containerVisual = new ContainerVisual();
containerVisual.Transform = new ScaleTransform(scale, scale);
containerVisual.Children.Add(page.Visual);
RenderTargetBitmap bitmap = new RenderTargetBitmap(
(int)(width * scale), (int)(height * scale), 96, 96, PixelFormats.Default);
bitmap.Render(containerVisual);
...
}

Apply the Zoom in/out to Origin Image in WPF

I created an Image with this code
OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = ""; // Default file name
myImage = new Image();
try
{
Nullable<bool> result = dlg.ShowDialog();
if (result == true)
{
string sUri = #dlg.FileName;
Uri src = new Uri(sUri, UriKind.RelativeOrAbsolute);
BitmapImage bmp = new BitmapImage(src);
myImage.Source = bmp;
myImage.Width = 100;
myImage.Height = 100;
}
}
I can zoom in/out on this image
public void Scale(int i, Point center)
{
Matrix m = myImage.RenderTransform.Value;
if (i > 0)
m.ScaleAtPrepend(1.1, 1.1, center.X, center.Y);
else
m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, center.X, center.Y);
myImage.RenderTransform = new MatrixTransform(m);
}
but when I get the ActualWidth or Width or ActualHeight/Height returns the number 100. this means that these changes not applied to origin image (myImage).
Now How to apply the changes (zoom or any changes) to origin Image?
Tanx all;
Transforms only affect the appearance / layout of the object they are applied to and won't make any changes directly to the source. For this you'll need to resize the image yourself, reading the values of the scaling and applying those to the original width and height.
Take a look at something like AForge.Net which has myriad methods for manipulating images allowing control over the quality of the transformation etc.

Categories

Resources