Fill a rectangle with an image using Mono Cairo Library - c#

I am trying to fill an image inside a rectangle. I was able to set the image position correctly to the leftmost corner of the rectangle. However the scaling does not work as expected. Any help on this is appreciated. Below is my code. This is a 1290*1990 dimensions image.
Cairo.Rectangle imageRectangle = new Cairo.Rectangle(50, 100, width, height);
ctx.NewPath();
Cairo.ImageSurface imgSurface = new Cairo.ImageSurface("C:/Temp/Image.png");
ctx.SetSource(imgSurface, topLeftPoint); //topLeft is (50,100)
float xScale = (float)imageRectangle.Width / (float)imgSurface.Width;
float yScale = (float)imageRectangle.Height / (float)imgSurface.Height;
//Reposition the image to the rectangle origin
ctx.Translate(imageRectangle.X, imageRectangle.Y);
ctx.Scale(xScale, yScale);
ctx.Paint();
Thanks!!

I found the solution. I was setting the source at the wrong place. Below is the right code
Cairo.Rectangle imageRectangle = new Cairo.Rectangle(50, 100, width, height);
ctx.NewPath();
Cairo.ImageSurface imgSurface = new Cairo.ImageSurface("C:/Temp/Image.png");
float xScale = (float)imageRectangle.Width / (float)imgSurface.Width;
float yScale = (float)imageRectangle.Height / (float)imgSurface.Height;
//Reposition the image to the rectangle origin
ctx.Translate(imageRectangle.X, imageRectangle.Y);
ctx.Scale(xScale, yScale);
ctx.SetSource(imgSurface);
ctx.Paint();
Thanks!

Related

Win2D draw rotated image on canvas

I'm trying to automatically create a photo collage with Win2D library. I've defined a couple of methods to do that:
ICanvasImage RotateImage(ICanvasImage photo, Size size, float degrees) returns a rotated image. I need to pass the image, its size and the degrees number. This method works for other things so I don't think it's that the problem.
ICanvasImage AddImage(ICanvasImage image, Size size, float x, float y, float degrees) it's the guilty. It draws the image on a CanvasRenderTarget with the specified position and size. It first call RotateImage to get the photo with the correct rotation and then draw it on the canvas.
At the end I use a method to save the content of the canvas as an image but it's not important, it works...
If I draw an horizontal image (no rotation => 0 degrees) I have no problem because the destination rectangle to pass to renderer.drawImage() is perfectly align with the photo. If I specified any angle the the image comes rotated correctly but the destination rectangle keeps horizontal an then the photo get cropped where it's out of the bounds of the rectangle.
This is my code:
//photo => photo to rotate
//size => size of the photo
//degrees => degrees to rotate the photo
ICanvasImage RotateImage(ICanvasImage photo, Size size, float degrees)
{
double height = Math.Sqrt(size.Width * size.Width + size.Height*size.Height);
//convert degreese to radians
float radians = (float)(degrees * Math.PI / 180d);
//get x,y where to place the rotated image
float y = (float)((height - size.Height) / 2.0f);
float x = (float)((height - size.Width) / 2.0f);
Vector2 endpoint = new Vector2((float)size.Width / 2, (float)size.Height / 2);
ICanvasImage image = new Transform2DEffect
{
Source = photo,
TransformMatrix = Matrix3x2.CreateRotation(radians, endpoint)
};
return image;
}
//size => final size that I want to have
//x,y => position of the photo
ICanvasImage AddImage(ICanvasImage image, Size size, float x, float y, float degrees)
{
//get the rotated image
ICanvasImage rotatedImage = RotateImage(image, size, degrees);
//start to draw in the canvas
using (var ds = renderer.CreateDrawingSession())
{
//destination rectangle with the specified position and size
Rect destRect = new Rect(x,y,size.Width,size.Height);
//source rectangle of the photo
Rect sourceRect = new Rect(0,0,rotatedImage.GetBounds(renderer).Width,rotatedImage.GetBounds(renderer).Height);
//draw the image on the canvas
ds.DrawImage(rotatedImage,destRect, sourceRect);
}
//here I call the method to save canvas' content as an image...
//you can simply try drawing directly on a canvasControl
}

Transforming Rectangle Coordinates to a Large Resolution Image

I'm using the following code to Transform a small rectangle coordinates to a larger one ie: A rectangle position on a small image to the same position on the larger resolution of the same image
Rectangle ConvertToLargeRect(Rectangle smallRect, Size largeImageSize, Size smallImageSize)
{
double xScale = (double)largeImageSize.Width / smallImageSize.Width;
double yScale = (double)largeImageSize.Height / smallImageSize.Height;
int x = (int)(smallRect.X * xScale + 0.5);
int y = (int)(smallRect.Y * yScale + 0.5);
int right = (int)(smallRect.Right * xScale + 0.5);
int bottom = (int)(smallRect.Bottom * yScale + 0.5);
return new Rectangle(x, y, right - x, bottom - y);
}
But there seems to be a problem with some images.The transformed rectangle coordinates seems to be off the image.
UPDATE:
img.Draw(rect, new Bgr(232, 3, 3), 2);
Rectangle transret= ConvertToLargeRect(rect, orgbitmap.Size, bit.Size);
target = new Bitmap(transret.Width, transret.Height);
using (Graphics g = Graphics.FromImage(target))
{
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawImage(orgbitmap, new Rectangle(0, 0, target.Width, target.Height),
transret, GraphicsUnit.Pixel);
}
Rectangle Drawn on small resolution Image
{X=190,Y=2,Width=226,Height=286}
Rectangle Transformed into Orginal Large Resolution Image {X=698,Y=7,Width=830,Height=931}
Original Image
First of all, if you resize the shape it shouldn't move position. That's not what one would expect out of enlarging a shape. This means the X,Y point of the top-left corner shouldn't be transformed.
Second, you shouldn't be adding 0.5 manually to operations, that's not a clean way to proceed. Use the ceiling function as suggested by #RezaAghaei
Third, you should not substract X/Y from the height/width, your calculations should be done as width * scale.
Please correct those mistakes, and if it doesn't work I'll update the answer with extra steps.

Crop a diagonal area from an image in WPF

I want to crop from an image using user-drawn rectangles on a canvas. The rectangles can be moved, re-sized, and rotated.
When the user selects "Get Cropped Image", the area inside the rectangle should be saved in a second image location on the page, which I can do perfectly well, so long as the rectangle is not rotated. (Straight-forward use of CroppedBitmap.) However, when the rectangle is at an angle I do not know how to perform the crop.
This is what I want to do (forgive my poor MS Paint skills):
My questions are:
1) How do I correctly track or calculate the points of the rectangle?
and,
2) Once I have the points, how do I crop the rotated rectangle?
EDIT:
Thanks to user Rotem, I believe that I have the answer to the second question. Using code modified from the following answers: Answer 1, Answer 2, I am seeing good results. Unfortunately, I am still unable to track the correct location points for the rectangle, so I cannot fully test this as of yet.
public static Bitmap CropRotatedRect(Bitmap source, System.Drawing.Rectangle rect, float angle, bool HighQuality)
{
Bitmap result = new Bitmap((int)rect.Width, (int)rect.Height);
using (Graphics g = Graphics.FromImage(result))
{
g.InterpolationMode = HighQuality ? InterpolationMode.HighQualityBicubic : InterpolationMode.Default;
using (Matrix mat = new Matrix())
{
mat.Translate(-rect.Location.X, -rect.Location.Y);
mat.RotateAt(-(angle), rect.Location);
g.Transform = mat;
g.DrawImage(source, new System.Drawing.Point(0, 0));
}
}
return result;
}
EDIT:
The answer to the first point is much easier than I had originally thought. You can always get the top-left corner of the rectangle by calling—
double top = Canvas.GetTop(rect);
double left = Canvas.GetLeft(rect);
You can then calculate the rest of the points using the width and the height—
Point topLeft = new Point(left, top);
Point topRight = new Point(left + rect.Width, top);
Point bottomLeft = new Point(left, top + rect.Height);
Point bottomRight = new Point(left + rect.Width, top + rect.Height);
Point centerPoint = new Point(left + (rect.Width / 2), top + (rect.Height / 2));
If your rectangle is rotated, then you have to translate these points to determine where they truly lie on the canvas—
public Point TranslatePoint(Point center, Point p, double angle)
{
// get the point relative to (0, 0) by subtracting the center of the rotated shape.
Point relToOrig = new Point(p.X - center.X, p.Y - center.Y);
double angleInRadians = angle * Math.PI / 180;
double sinOfA = Math.Sin(angleInRadians);
double cosOfA = Math.Cos(angleInRadians);
Point translatedPoint = new Point(relToOrig.X * cosOfA - relToOrig.Y * sinOfA,
relToOrig.X * sinOfA + relToOrig.Y * cosOfA);
return new Point(translatedPoint.X + center.X, translatedPoint.Y + center.Y);
}
Once you are able to translate the top-left corner, you can use Rotem's cropping method. You can also calculate the position of the rest of the rectangle, so you are able to determine if the rectangle is within the bounds of the image, if it is touching an edge, or any other thing that you might want to do in regards to the position.
I discovered the answer to my own question(s), and made the appropriate edits along the way. Please see above for the answer.

How to scale a image based on its rotation

I want to scale an image so that the image is always the size of the screen no matter how it is rotated. Does anyone have any ideas on going about this? By the way I am programing this in C# with xna.
Perhaps something similiar to this, although I'm unsure how you expect to draw the texture. It would be easiest by using triangles and texture wrapping them.
This is how I got the new width and new height after rotating:
Matrix origin = Matrix.CreateTranslation(0, 0, 0);
Matrix scale = Matrix.CreateScale(1f);
Matrix rotation = Matrix.CreateRotationZ(MathHelper.ToRadians(rotate));
Matrix translation = Matrix.CreateTranslation(0, 0, 0);
Vector2 pos1 = Vector2.Transform(new Vector2(Texture.Width / 2, Texture.Height / 2), origin * scale * rotation * origin);
Vector2 pos2 = Vector2.Transform(new Vector2(Texture.Width, Texture.Height), origin * scale * rotation * translation);
int width = (int)Math.Abs(pos2.X - pos1.X) * 2;
int height = (int)Math.Abs(pos2.Y - pos1.Y) * 2;
float scaleX = (graphics.PreferredBackBufferWidth / width);
float scaleY = (graphics.PreferredBackBufferHeight / height);
You will probably figure out the best way to draw this, because an image flipped 45 degrees will look weird drawn on the screen so you probably have to scale it up so it fits the screen but still be rotated. That you left out, an image rotated 180 degrees or 90 degrees should work better.
You can apply a RotateTransform to transform the image, then enclose that in a LayoutTransform to fill the dimensions of the container (the screen in this case).

C# drawing Rectangle with precision coordinates. How?

How do I draw a Rectangle in C# with precision coordinates?
Example:
Rectangle test1 = new Rectangle(X, Y, Width, Height);
Width and Height values seem to have to be integers.
Is it possible to give Rectangle, inch size coordinates somehow?
Rectangle test2 = new Rectangle(100, 50, 1.93inches, 0.52inches);
Thanks for any help.
You'd have to convert know the conversion between inches and pixels, which depends on the screen resolution. I suppose you could do this:
Graphics graphics = this.CreateGraphics();
var dpiX = graphics.DpiX;
var dpiY = graphics.DpiY;
var rectangle = new Rectangle(100, 50, Math.Round(1.93 * dpiX), Math.Round(0.52 * dpiY));

Categories