I'm trying to rotate image but couldn't get expected result.
I've tried with WIn2D but couldn't make image as expected.
My tried Code
public async void Rotate(string originalImagepath, Rect originalImageRect, float degrees)
{
int height = (int)Math.Sqrt(originalImageRect.Width * originalImageRect.Width + originalImageRect.Height * originalImageRect.Height);
CanvasDevice device = CanvasDevice.GetSharedDevice();
CanvasRenderTarget webCardImage = null;
CanvasBitmap bitmap = null;
var logicalDpi = DisplayInformation.GetForCurrentView().LogicalDpi;
Vector2 endpoint = new Vector2((float)originalImageRect.Width / 2, (float)originalImageRect.Height / 2);
try
{
webCardImage = new CanvasRenderTarget(device, height, height, logicalDpi);
using (var ds = webCardImage.CreateDrawingSession())
{
ds.Clear(Colors.Transparent);
using (FileStream imageStream = new FileStream(originalImagepath, FileMode.Open))
{
IRandomAccessStream fileStream = imageStream.AsRandomAccessStream();
bitmap = await CanvasBitmap.LoadAsync(device, fileStream);
}
ICanvasImage image = new Transform2DEffect
{
Source = bitmap,
TransformMatrix = Matrix3x2.CreateRotation(degrees, endpoint),
};
var sourceRect = image.GetBounds(ds);
ds.DrawImage(image, new Rect(originalImageRect.X, originalImageRect.Y, originalImageRect.Width, originalImageRect.Height), sourceRect, 1, CanvasImageInterpolation.HighQualityCubic);
}
}
catch (Exception ex)
{
}
//Save Image Code here
}
Expected Output:
My generated Image:
NB: Rect originalImageRect refers main image rect which i want to rotate.
When this happens, it means that after your image is rotated, the size of the target rendering rectangle (may be height or width) is smaller than the size of the rotated image, resulting in compression of the image.
I analyzed your code, you tried to create a square with the originalImageRect diagonal length as the side length, to ensure that the image will not exceed the rendering range when rotating around the center.
This is good, but there are some deviations when rendering.
The image is in the upper left corner of CanvasRenderTarget by default. If no displacement is performed, the center point is on the upper left when rotating, which means it will still exceed the rendering range.
You can try this:
double height = Math.Sqrt(originalImageRect.Width * originalImageRect.Width + originalImageRect.Height * originalImageRect.Height);
float y = (float)((height - originalImageRect.Height) / 2.0f);
float x = (float)((height - originalImageRect.Width) / 2.0f);
Vector2 endpoint = new Vector2((float)height / 2, (float)height / 2);
try
{
webCardImage = new CanvasRenderTarget(MyCanvas, (float)height, (float)height, logicalDpi);
using (var ds = webCardImage.CreateDrawingSession())
{
// init CanvasBitmap
ICanvasImage image = new Transform2DEffect
{
Source = bitmap,
TransformMatrix = Matrix3x2.CreateTranslation(new Vector2(x,y))
* Matrix3x2.CreateRotation(degrees,endpoint)
};
var sourceRect = image.GetBounds(ds);
ds.DrawImage(image);
}
}
catch (Exception ex)
{
}
We first shift the image, move it to the center of the canvas, and then rotate it around the center of the canvas, so that we can achieve our goal.
In addition, when using the DrawImage() method, there is no need to additionally define Rect for rendering constraints.
Thanks.
Related
I'm trying to align a logo in the middle of a SKBitmap.
Here is my code:
SKBitmap bmpResult = SKBitmap.Decode(imgByteArray);
bitmap.DrawBitmap(bmpResult, 0, 20);
I already tried with different images, but the result is the same.
I know that SkiaSharp has MeasureText, but, as it says, for text, not for image.
I tried to get my device width with DeviceDisplay.MainDisplayInfo.Width and divide by 2, but no result.
NOTE that, even if I get a exactly number to divide (for example: bitmap.DrawBitmap(bmpResult, DeviceDisplay.MainDisplayInfo.Width / 2, 20);), when I have different screen densities, the final result will be different, that is, align with one device, unalign with other.
Is anyone knows how can I fix it?
You could create two bitmaps(bitmap1, bitmap2). Draw the bitmap1 first and then draw the bitmap2. I make two bitmaps in center for your reference.
public partial class Page8 : ContentPage
{
SKBitmap bitmap1 = new SKBitmap();
SKBitmap bitmap2 = new SKBitmap();
public Page8()
{
InitializeComponent();
bitmap1 = LoadBitmapResource(typeof(Page8), "App3.image.jpeg");
bitmap2 = LoadBitmapResource(typeof(Page8), "App3.durian_64.png");
// Create the SKCanvasView and set the PaintSurface handler
SKCanvasView canvasView = new SKCanvasView();
canvasView.PaintSurface += OnCanvasViewPaintSurface; ;
Content = canvasView;
}
private void OnCanvasViewPaintSurface(object sender, SKPaintSurfaceEventArgs args)
{
SKImageInfo info = args.Info;
SKSurface surface = args.Surface;
SKCanvas canvas = surface.Canvas;
canvas.Clear();
float x1 = (info.Width - bitmap1.Width) / 2;
float y1 = (info.Height - bitmap1.Height) / 2;
float x2 = (info.Width - bitmap2.Width) / 2;
float y2 = (info.Height - bitmap2.Height) / 2;
canvas.DrawBitmap(bitmap1, x1, y1);
canvas.DrawBitmap(bitmap2, x2, y2);
}
public static SKBitmap LoadBitmapResource(Type type, string resourceID)
{
Assembly assembly = type.GetTypeInfo().Assembly;
using (Stream stream = assembly.GetManifestResourceStream(resourceID))
{
return SKBitmap.Decode(stream);
}
}
}
In the screenshot, you would see the bitmap2(green durian) in the center of the bitmap1.
I am capturing InkStrokes and have a need to create a scaled bitmap image of the strokes in the background. The captured images need to be of uniform size regardless of how big the bounding box of the ink.
For example, if original ink stroke is drawn and the bounding box top/left is 100,100 and size is 200,200 on the ink canvas, I want the ink to start at 0,0 of the new rendered bitmap that is 50,50 size (ignore impact of stroke width right now).
I have figured out how to scale the ink strokes (thanks StackOverflow) but not how to move the strokes. Right now, it seems I have to create a bitmap the size of the InkCanvas, render the scaled ink, then crop bigger image to the correct size.
I've tried using the InkStroke.PointTranslate via
var scaleMatrix = Matrix3x2.CreateScale(scale);
scaleMatrix.Translation = -offset; // top/left of ink stroke bounding box
stroke.PointTransform = scaleMatrix;
But the coordinates do not come out correct.
Any help much appreciated.
You can combine transformations by multiplying matrices. This works for me
var strokes = inkCanvas.InkPresenter.StrokeContainer.GetStrokes();
var boundingBox = inkCanvas.InkPresenter.StrokeContainer.BoundingRect;
var matrix1 = Matrix3x2.CreateTranslation((float)-boundingBox.X, (float)-boundingBox.Y);
var matrix2 = Matrix3x2.CreateScale(0.5f);
var builder = new InkStrokeBuilder();
var newStrokeList = new List<InkStroke>();
foreach (var stroke in strokes)
{
newStrokeList.Add(builder.CreateStrokeFromInkPoints
(stroke.GetInkPoints(), matrix1 * matrix2));
}
//Add the translated and scaled strokes to the inkcanvas
inkCanvas.InkPresenter.StrokeContainer.AddStrokes(newStrokeList);
Maybe I was still doing something wrong, but it appears you cannot use InkStrokeBuilder.CreateStrokeFromInkPoints with more than one kind of transform. I tried all kinds of combinations/approaches, and just could not get it to work.
Here is my solution...
private static IList<InkStroke> GetScaledAndTransformedStrokes(IList<InkStroke> strokeList, float scale)
{
var builder = new InkStrokeBuilder();
var newStrokeList = new List<InkStroke>();
var boundingBox = strokeList.GetBoundingBox();
foreach (var singleStroke in strokeList)
{
var translateMatrix = new Matrix(1, 0, 0, 1, -boundingBox.X, -boundingBox.Y);
var newInkPoints = new List<InkPoint>();
var originalInkPoints = singleStroke.GetInkPoints();
foreach (var point in originalInkPoints)
{
var newPosition = translateMatrix.Transform(point.Position);
var newInkPoint = new InkPoint(newPosition, point.Pressure, point.TiltX, point.TiltY, point.Timestamp);
newInkPoints.Add(newInkPoint);
}
var newStroke = builder.CreateStrokeFromInkPoints(newInkPoints, new Matrix3x2(scale, 0, 0, scale, 0, 0));
newStrokeList.Add(newStroke);
}
return newStrokeList;
}
I ended up having to apply my own translate transform then use the builder.CreateStrokeFromInkPoints with a scale matrix applied to get the results I wanted. GetBoundingBox is my own extension:
public static class RectExtensions
{
public static Rect CombineWith(this Rect r, Rect rect)
{
var top = (r.Top < rect.Top) ? r.Top : rect.Top;
var left = (r.Left < rect.Left) ? r.Left : rect.Left;
var bottom = (r.Bottom < rect.Bottom) ? rect.Bottom : r.Bottom;
var right = (r.Right < rect.Right) ? rect.Right : r.Right;
var newRect = new Rect(new Point(left, top), new Point(right, bottom));
return newRect;
}
}
I'm using C# to scale and rotate an image with a transparent background, then using JavaScript to add it to a Google Maps API map, but once it's scaled and rotated, the edges of the image get blurred, and it becomes hard to see, especially over water.
Here is my original 300x300 image:
And here is an example of one of the output images at 100x100 (SizeInt = 100):
Before you get confused as to the background colour, the image is projected onto a map where the water is a similar colour to the image.
You can see some slight ghosting around the edges of the image.
Once it's scaled down further (to it's required scale):
It's almost unnoticeable...
I'm still relatively new to C#, but I've tried loading SVGs directly onto the map with JavaScript (icon: "some/SVG/file.svg"), but that causes serious performance problems, as there are up to 1,000 icons on the map at any given time, making the map unusable when using SVG files, so I have to use image files.
The images need to be sharp and not blurred around the edges. Here is the code used to generate the images, forgive any sloppy code, I'm still somewhat new to C#:
Here is the code used to scale the image:
The image needs to be rotated in a full circle (rounded to 5 degrees), hence the loop from 0 to 361.
private void BuildIcons(string dir)
{
int SizeInt = 30;
// Get the content path
var ContentPath = dir;
// Get the original image
Bitmap OriginalImage = new Bitmap(ContentPath + "base.png");
#region Resize image
// New size
Size size = new Size(SizeInt, SizeInt);
// New height/width temp vars
int newWidth;
int newHeight;
// Aspect ratio preservation
int originalWidth = OriginalImage.Width;
int originalHeight = OriginalImage.Height;
float percentWidth = (float)size.Width / (float)originalWidth;
float percentHeight = (float)size.Height / (float)originalHeight;
float percent = percentHeight < percentWidth ? percentHeight : percentWidth;
newWidth = (int)(originalWidth * percent);
newHeight = (int)(originalHeight * percent);
// Generate the new image
Image ResizedImage = new Bitmap(newWidth, newHeight);
using (Graphics graphicsHandle = Graphics.FromImage(ResizedImage))
{
graphicsHandle.SmoothingMode = SmoothingMode.HighQuality;
graphicsHandle.InterpolationMode = InterpolationMode.HighQualityBicubic;
graphicsHandle.PixelOffsetMode = PixelOffsetMode.HighQuality;
graphicsHandle.DrawImage(OriginalImage, 0, 0, newWidth, newHeight);
}
#endregion
int maxside = SizeInt;
#region Image rotation
// Generate images
for (int i = 0; i < 361; i += 5)
{
// New empty bitmap to hold rotated images
Bitmap ReturnBitmap = new Bitmap(maxside, maxside);
// Graphics object from the empty bitmap
// Normal
Graphics Gfx = Graphics.FromImage(ReturnBitmap);
Gfx.SmoothingMode = SmoothingMode.HighQuality;
Gfx.InterpolationMode = InterpolationMode.HighQualityBicubic;
Gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
// Move rotation point to center of image
Gfx.TranslateTransform(
(float)ResizedImage.Width / 2,
(float)ResizedImage.Height / 2
);
// Rotate
Gfx.RotateTransform(i);
// Move image back
Gfx.TranslateTransform(
-(float)ResizedImage.Width / 2,
-(float)ResizedImage.Height / 2
);
// draw original in image onto graphics object
Gfx.DrawImage(ResizedImage, new Point(0, 0));
// Save the image
ReturnBitmap.Save(ContentPath + i.ToString() + ".png");
// Clean up
ReturnBitmap.Dispose();
Gfx.Dispose();
}
#endregion
// Clean up
ResizedImage.Dispose();
OriginalImage.Dispose();
}
Any suggestions?
I'am using PictureBox for displaying images. My images are direct from scanner so the resolutions are up to 4000*4000... Because my display area is a lot smaller I have to display the image with pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; to preserve aspect ratio.
After that the image is in the middle of the screen.
How can I find the distance between the left side of the image control and the REAL left side of an actual image (see image bellow).
Is there any solution?
Btw. displaying the image on the left side of the screen would do the trick too.
var imageHeight = pictureBox1.Image.Height;
var imageWidth = pictureBox1.Image.Width;
var userSelection = rect.Rect;
var display = pictureBox1.DisplayRectangle;
var xFactor = (float)userSelection.Width / display.Width;
var yFactor = (float)userSelection.Height / display.Height;
var realCropSizeWidth = xFactor * imageWidth;
var realCropSizeHight = yFactor * imageHeight;
var realCropX = imageWidth / display.Width;
realCropX *= userSelection.X;
var realCropY = imageHeight / display.Height;
realCropY *= userSelection.Y;
var realCropRectangle = new Rectangle(realCropX, realCropY, (int)realCropSizeWidth,
(int)realCropSizeHight);
var image = CropImage(pictureBox1.Image, realCropRectangle);
pictureBox1.Image = image;
public Image CropImage(Image source, Rectangle rectangle)
{
var target = new Bitmap(rectangle.Width, rectangle.Height);
using (var g = Graphics.FromImage(target))
{
g.DrawImage(source, new Rectangle(0, 0, target.Width, target.Height),
rectangle,
GraphicsUnit.Pixel);
}
return target;
}
As far as I know there is no direct way of getting what you want, but some simple maths would suffice:
You know the aspect ratio of your original image which is preserved and you know the aspect ratio of your picturebox in which you are showing it. Based on that you can figure out which dimension (height or width) the image fits exactly. Once you know that you can obtain the scaling factor of the image and therefore you can calculate the other dimension of the shown image.
As the image will be centered on the dimension that is not fitted exactly to the picturebox, its straighforward to get the distance you are looking for.
I have an image I display on my website. Which is written in c#. I want to give my user the ability to click on a button which rotates the image. This will rotate the actual image on the server so next time it is displayed it is displayed the correct way.
Similar to how facebook has image rotation?
Do you really need to rotate the image on the server? Why not just store a property with the image which stores the rotation value like 90, 180, 270... and apply this every time image is retrieved and update/save the property value once user rotates the image
see this tutorial for how to rotate an image or google it you will find a lot of samples
//Create Image element
Image rotated270 = new Image();
rotated270.Width = 150;
//Create source
BitmapImage bi = new BitmapImage();
//BitmapImage properties must be in a BeginInit/EndInit block
bi.BeginInit();
bi.UriSource = new Uri(#"pack://application:,,/sampleImages/watermelon.jpg");
//Set image rotation
bi.Rotation = Rotation.Rotate270;
bi.EndInit();
//set image source
rotated270.Source = bi;
public static Image RotateImage(Image image, Size size, float angle)
{
if (image == null)
{
throw new ArgumentNullException("image");
}
if (size.Width < 1 || size.Height < 1)
{
throw new ArgumentException("size must be larger than zero.");
}
Bitmap tempImage = new Bitmap(size.Width, size.Height);
using (Graphics tempGraphics = Graphics.FromImage(tempImage))
{
PointF center = new PointF((float)size.Width / 2F, (float)size.Height / 2F);
tempGraphics.TranslateTransform(center.X, center.Y, MatrixOrder.Prepend);
tempGraphics.RotateTransform(angle != 180F ? angle : 182F/*at 180 exact angle the rotate make a small shift of image I don't know why!*/);
tempGraphics.TranslateTransform(-center.X, -center.Y, MatrixOrder.Prepend);
tempGraphics.DrawImage(image, new PointF());
}
return tempImage;
}