I am trying to implement a custom painter that can draw an image (scaled down version) on the canvas and the drawn image can be rotated and scaled.
I get to know that to scale the image I have to scale the canvas using scale method.
Now the questions is how to rotate the scaled image on its center (or any other point). The rotate method of canvas allow only to rotate on top left corner.
Here is my implementation that can be extended
Had the same problem, Solution was simply making your own rotation method in three lines
void rotate(Canvas canvas, double cx, double cy, double angle) {
canvas.translate(cx, cy);
canvas.rotate(angle);
canvas.translate(-cx, -cy);
}
We thus first move the canvas towards the point you want to pivot around. We then rotate along the the topleft (default for Flutter) which in coordinate space is the pivot you want and then put the canvas back to the desired position, with the rotation applied. Method is very efficient, requiring only 4 additions for the translation and the rotation cost is identical to the original one.
This can achieve by shifting the coordinate space as illustrated in figure 1.
The translation is the difference in coordinates between C1 and C2, which are exactly as between A and B in figure 2.
With some geometry formulas, we can calculate the desired translation and produce the rotated image as in the method below
ui.Image rotatedImage({ui.Image image, double angle}) {
var pictureRecorder = ui.PictureRecorder();
Canvas canvas = Canvas(pictureRecorder);
final double r = sqrt(image.width * image.width + image.height * image.height) / 2;
final alpha = atan(image.height / image.width);
final beta = alpha + angle;
final shiftY = r * sin(beta);
final shiftX = r * cos(beta);
final translateX = image.width / 2 - shiftX;
final translateY = image.height / 2 - shiftY;
canvas.translate(translateX, translateY);
canvas.rotate(angle);
canvas.drawImage(image, Offset.zero, Paint());
return pictureRecorder.endRecording().toImage(image.width, image.height);
}
alpha, beta, angle are all in radian.
Here is the repo of the demo app
If you don't want to rotate the image around the center of the image you can use this way. You won't have to care about what the offset of the canvas should be in relation to the image rotation, because the canvas is moved back to its original position after the image is drawn.
void rotate(Canvas c, Image image, Offset focalPoint, Size screenSize, double angle) {
c.save();
c.translate(screenSize.width/2, screenSize.height/2);
c.rotate(angle);
// To rotate around the center of the image, focal point is the
// image width and height divided by 2
c.drawImage(image, focalPoint*-1, Paint());
c.translate(-screenSize.width/2, -screenSize.height/2);
c.restore();
}
Related
How do I convert pixel/screen coordinates to cartesian coordinates(x,y)?
The info I have on the pictures is (see image below):
vFov in degrees, hFov in degrees, pixel width, pixel height
Basically what I want is to take any pixel on the image, and calculate the pixel position from image center in degrees.
For my answer I assume that your image represents a projection onto a planar surface.
Then a virtual camera can be constructed such that it sees the width/height of the image in exactly the right field of view. To get the distance d between the image and the camera(in pixels) a constructed right triangle can be used:
tan(FOV/2) = width/2 / d → d = width/(2tan(FOV/2))
The same equation should hold for the height.
In a similar way the angle of the pixel can be calculated(assuming the center of the image is (0, 0)):
tan(angleX) = x/d → angleX = arctan(x/d) = arctan(x/width * 2tan(hFov))
tan(angleY) = y/d → angleY = arctan(y/d) = arctan(y/width * 2tan(vFov))
In case the image is warped the d's of the vertical and the horizontal can be different and therefor you should not precalculate d.
I am trying to find the simplest way to rotate and display a TBitmap on its center by any given angle needed. The TBitmap is square and any clipping that might occur is not important so long as the rotated bitmap's center point remains constant. The image is very small, only around 50 x 50 pixels so speed isn't an issue. Here is the code I have so far which rotates a TBitmap to 90 degrees, which is simple, the any angle thing less so.
std::auto_ptr<Graphics::TBitmap> bitmap1(new Graphics::TBitmap);
std::auto_ptr<Graphics::TBitmap> bitmap2(new Graphics::TBitmap);
bitmap1->LoadFromFile("c:/myimage.bmp");
bitmap1->Transparent = true;
bitmap1->TransparentColor = bitmap1->Canvas->Pixels[50][50];
bitmap2->Width=bitmap1->Height;
bitmap2->Height=bitmap1->Width;
double x1 = 0.0;
double y1 = 0.0;
for (int x = 0;x < bitmap1->Width; x++)
{
for(int y = 0;y < bitmap1->Height;y++)
{
x1 = std::cos(45.0) * x - std::sin(45.0) * y;
y1 = sin(45.0) * x + cos(45.0) * y;
bitmap2->Canvas->Pixels[x1][y1] =
bitmap1->Canvas->Pixels[x][y];
}
}
Form1->Canvas->Draw( 500, 200, bitmap2.get());
See revised code... This allows for rotation but the copy creates a hazy image and the rotation point is at the top left.
you are doing this the other way around so there may be present holes in the resulting image because you are looping the source pixels with 1 pixel step .... to remedy this loop the target pixels instead...
loop through bitmap2 pixels (x2,y2)
for each compute rotated-back (x1,y1) position in bitmap1
copy pixel value
if (x1,y1) is outside bitmap1 then use backgroun color like clBlack instead.
To improve speed use TBitmap->ScanLine[y] property that will improve speed at least 1000x times if used right see:
Display an array of color in C
After I put all this together I got this:
#include <math.h> // just for cos,sin
// rotate src around x0,y0 [pixels] by angle [rad] and store result in dst
void rotate(Graphics::TBitmap *dst,Graphics::TBitmap *src,double x0,double y0,double angle)
{
int x,y,xx,yy,xs,ys;
double s,c,fx,fy;
// resize dst to the same size as src
xs=src->Width;
ys=src->Height;
dst->SetSize(xs,ys);
// allow direct pixel access for src
src->HandleType=bmDIB;
src->PixelFormat=pf32bit;
DWORD **psrc=new DWORD*[ys];
for (y=0;y<ys;y++) psrc[y]=(DWORD*)src->ScanLine[y];
// allow direct pixel access for dst
dst->HandleType=bmDIB;
dst->PixelFormat=pf32bit;
DWORD **pdst=new DWORD*[ys];
for (y=0;y<ys;y++) pdst[y]=(DWORD*)dst->ScanLine[y];
// precompute variables
c=cos(angle);
s=sin(angle);
// loop all dst pixels
for (y=0;y<ys;y++)
for (x=0;x<xs;x++)
{
// compute position in src
fx=x; // convert to double
fy=y;
fx-=x0; // translate to center of rotation
fy-=y0;
xx=double(+(fx*c)+(fy*s)+x0); // rotate and translate back
yy=double(-(fx*s)+(fy*c)+y0);
// copy pixels
if ((xx>=0)&&(xx<xs)&&(yy>=0)&&(yy<ys)) pdst[y][x]=psrc[yy][xx];
else pdst[y][x]=0; // black
}
// free memory
delete[] psrc;
delete[] pdst;
}
usage:
// init
Graphics::TBitmap *bmp1,*bmp2;
bmp1=new Graphics::TBitmap;
bmp1->LoadFromFile("image.bmp");
bmp1->HandleType=bmDIB;
bmp1->PixelFormat=pf32bit;
bmp2=new Graphics::TBitmap;
bmp2->HandleType=bmDIB;
bmp2->PixelFormat=pf32bit;
// rotate
rotate(bmp2,bmp1,bmp1->Width/2,bmp1->Height/2,25.0*M_PI/180.0);
// here render bmp2 or whatever
// exit
delete bmp1;
delete bmp2;
Here example output:
On the left is bmp1 and on the right rotated bmp2
I need to define a rotated rectangle from its 4 corners. The rotated rectangle is defined by a center point, a size couple (width, height), and an angle.
How is it decided which size is the height, and which one is the width?
The width is not the length of the most horizontal edge, is it? E.g. if the angle is bigger than 90°, does it swap?
height should be the largest side, width is the other one, and angle is the rotation angle (in degrees) in a clockwise direction.
Otherwise, you can get an equivalent rectangle with height and width swapped, rotated by 90 degrees.
You can use minAreaRect to find a RotatedRect:
vector<Point> pts = {pt1, pt2, pt3, pt4}
RotatedRect box = minAreaRect(pts);
// Be sure that largest side is the height
if (box.size.width > box.size.height)
{
swap(box.size.width, box.size.height);
box.angle += 90.f;
}
Ok, with Miki's help, and with some tests, I got it clearer...
It seems that the rotated rectangle is an upright rectangle (width and height are clearly defined, then)... that is rotated!
In image coords, y is directed to the bottom, the angle is given clockwise. In usual math coords (y to the top), the angle is given counter-clockwise. Then, it fits with c++ <math.h> included atan2(y,x) function for example (except that it returns radians).
Then, to summarize, if we consider one given edge of the rectangle (two corners), its length can be considered as the width if we retrieve the angle with atan2 on its y difference and x difference. Something like:
Point pt1, pt2, pt3, pt4;
RotatedRect rect;
rect.center = (pt1 + pt2 + pt3 + pt4)/4;
// assuming the points are already sorted
rect.size.width = distance(pt1, pt2); // sqrt(...)
rect.size.height = distance(pt2, pt3);
rect.angle = atan2(pt2.y-pt1.y, pt2.x-pt1.x);
and this can be improved with width being the mean value of dist(pt1,pt2) and dist(pt3,pt4) for example. The same for height.
angle can also be calculated as being the mean value of atan for (pt1, pt2) and atan for (pt3, pt4).
My question is related to calculating position. Scale the INNER IMAGE in FRONT END then i need to find the relative position on same in BACKGROUND PROCESS. If any one experience in it please share here like an equation or something.
The FRONT END FRAME IMAGE have a size of 188x292(WidthxHeight)
and Larger FRAME IMAGE have the size of 500x750(WidthxHeight).
INNER IMAGE 75x75(WidthxHeight) and Larger INNER IMAGE
199.45x199.45(WidthxHeight)
Question : When i scale the INNER IMAGE in FRONT END. That is 75x75 to 100x100, then we have the x and y position of that. And i need to calculate the exact position in BACKGROUND PROCESS. It's for scale that image programatically.
After INNER IMAGE scale you will have x and y position for it, now convert it to %.
if position of INNER IMAGE is
relativeX = (x * 100)/frameImageWidth;
relativeY = (y * 100)/frameImageHeight;
position of INNER IMAGE for Background will be
x = (relativeX * backgroundFrameImageWidth)/100;
y = (relativeY * backgroundFrameImageHeight)/100;
CGPoint foregroundLocation = CGPointMake(x, y);
static float xscale = BACKGROUND_IMAGE_WIDTH / FOREGROUND_IMAGE_WIDTH;
static float yscale = BACKGROUND_IMAGE_HEIGHT / FOREGROUND_IMAGE_HEIGHT;
CGPoint backgroundLocation = CGPointApplyAffineTransform(foregroundLocation, CGAffineTransformMakeScale(xscale, yscale));
i'm trying to build a game in xna, i got a circle which i want the player to move around it, as you can see in the following picture, its working great except the drawing part which i'm not pleased with
here's a link to an image http://s12.postimage.org/poiip0gtp/circle.png
i want to center the player object to the edge of the circle so it won't look like the player is standing on air
this is how i calculate the position of the player
rad = (degree * Math.PI / 180);
rotationDegree = (float)((Math.PI * degree) / 180);
currentPosition.X = (float)(Math.Cos(rad) * Earth.radius + (GraphicsDevice.Viewport.Width / 2));
currentPosition.Y = (float)(Math.Sin(rad) * Earth.radius + (GraphicsDevice.Viewport.Height / 2));
and this is how i draw the player
spriteBatch.Draw(texture,currentPosition, null, Color.White,rotationDegree, Vector2.Zero,1f,SpriteEffects.None, 1f);
thank you.
Use the origin overload for spritebatch. Which is where the sprite is drawn according to the position.
Spritebatch.Draw(texture,Position, null,Color.White,0f,new Vector2(texture.Width / 2,texture.Height /2),1f,SpriteEffects.None, 0);
Using texture.Width / 2,texture.Height /2 for origin will center it.
It looks like what you want to do here is adjust the sprite's origin, which is the vector that you're passing into SpriteBatch.Draw(). This is used to determine the "center point" of your sprite; {0, 0} represents the sprite's upper-left corner, while {spriteWidth, spriteHeight} represents the bottom-right corner. Your sprite will be positioned and rotated relative to this origin.