I'm wondering about a line's exact width in JavaFX 2.1.
Creating a straight line starting at point (10;10) and ending at point (20;10) should have an expected width of 10px. When reading the line's width a value of 11px is returned.
When starting the attached example and making a screenshot you'll see - at a higher zoom level - the line has a width of 12px and a height of 2px.
Using a LineBuilder does not make any difference. I event tried to apply a different StrokeType without success.
Creating a square with a side length of 10 returns the expected width of 10px.
Why does line.getBoundsInLocal().getWidth() return different values from those I see in the rendered result (screenshot)?
Why is there a difference concerning width when creating lines and polygons?
Example:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Line;
import javafx.scene.shape.Polygon;
import javafx.stage.Stage;
public class ShapeWidthDemo extends Application {
#Override
public void start(Stage stage) throws Exception {
Group root = new Group();
int startX = 10;
int startY = 10;
int length = 10;
Line line = new Line(startX, startY, startX + length, startY);
System.out.println("line width: " + line.getBoundsInLocal().getWidth());
//->line width: 11.0
System.out.println("line height: " + line.getBoundsInLocal().getHeight());
//->line height: 1.0
root.getChildren().add(line);
int startY2 = startY + 2;
Polygon polygon = new Polygon(
startX, startY2,
startX + 10, startY2,
startX + 10, startY2 + 10,
startX, startY2 + 10);
System.out.println("polygon width: " + polygon.getBoundsInLocal().getWidth());
//->polygon width: 10.0
System.out.println("polygon height: " + polygon.getBoundsInLocal().getHeight());
//->polygon height: 10.0
root.getChildren().add(polygon);
stage.setScene(new Scene(root, 100, 100));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
1) The line's end points are controlled by StrokeLineCap.
So with default stroke width you can get the "desired" values by
line.setStrokeType(StrokeType.CENTERED);
line.setStrokeLineCap(StrokeLineCap.BUTT);
... that the line has a width of 12px and a height of 2px.
You can get these values if StrokeType.OUTSIDE AND NOT StrokeLineCap.BUTT are applied. However the height remains in 2px even changing the stroke attributes. That's strange..
2) Its because of the polygon's stroke value is NULL by default. By setting it like polygon.setStroke(Color.RED); the stroke will render.
Think of the line itself as having no surface area whatsoever, it is infinitely thin. The only dimensions for the line come from the stroke.
When the StrokeType is CENTERED and the StrokeLineCap is SQUARE (the default values), then the area taken by the the line comes from evenly applying half the stroke width around the line in all directions.
In your example, you are drawing a horizontal line with a stroke of 1 at an integer co-ordinate. The JavaFX co-ordinate system is such that integer corners map to the lines between pixels. When you display a horizontal line with integer co-ordinates, the line itself crosses half of the two vertical pixels on either side of the line and ends up as a gray shaded antialiased line rather than a black line shading only one vertical pixel. Additionally, the StrokeLineCap of SQUARE applied at the line ends means that the stroke extends beyond the end points of the line by half the stroke width. This ends up shading a quarter of each pixel in the two pairs of pixels on either side of the line ends.
By offsetting the co-ordinates of the line by half a pixel, the line can be drawn such that it shades a single row of vertical pixels as now the line is falling along the middle of each pixel and the line stroke is extending to the top and bottom edges of each pixel. Additionally, setting the line cap to butt rather than square means that the shaded area for the line will not extend beyond the ends of the line.
Here is some sample code to demonstrate this. In the sample, only the last line completely shades exactly 10 pixels and no more. It is also the only line which has a width of 10 and integer co-ordinates in it's bounding box.
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeLineCap;
import javafx.stage.Stage;
public class LineBounds extends Application {
public static void main(String[] args) { launch(args); }
#Override public void start(Stage stage) throws Exception {
double startX = 10;
double startY = 10;
double length = 10;
Line lineSpanningPixelsSquareEnd = new Line(startX, startY, startX + length, startY);
System.out.println("lineSpanningPixels (square) bounding box: " + lineSpanningPixelsSquareEnd.getBoundsInLocal());
startX = 10;
startY = 20;
length = 10;
Line lineSpanningPixelsButtEnd = new Line(startX, startY, startX + length, startY);
lineSpanningPixelsButtEnd.setStrokeLineCap(StrokeLineCap.BUTT);
System.out.println("lineSpanningPixels (butt) bounding box: " + lineSpanningPixelsButtEnd.getBoundsInLocal());
startX = 10;
startY = 29.5;
length = 10;
Line lineOnPixelsSquareEnd = new Line(startX, startY, startX + length, startY);
System.out.println("lineOnPixels (square) bounding box: " + lineOnPixelsSquareEnd.getBoundsInLocal());
startX = 10;
startY = 39.5;
length = 10;
Line lineOnPixelsButtEnd = new Line(startX, startY, startX + length, startY);
lineOnPixelsButtEnd.setStrokeLineCap(StrokeLineCap.BUTT);
System.out.println("lineOnPixels (butt) bounding box: " + lineOnPixelsButtEnd.getBoundsInLocal());
stage.setScene(
new Scene(
new Group(
lineSpanningPixelsSquareEnd, lineSpanningPixelsButtEnd, lineOnPixelsSquareEnd, lineOnPixelsButtEnd
), 100, 100
)
);
stage.show();
}
}
Output of the sample program is:
lineSpanningPixels (square) bounding box: BoundingBox [minX:9.5, minY:9.5, minZ:0.0, width:11.0, height:1.0, depth:0.0, maxX:20.5, maxY:10.5, maxZ:0.0]
lineSpanningPixels (butt) bounding box: BoundingBox [minX:10.0, minY:19.5, minZ:0.0, width:10.0, height:1.0, depth:0.0, maxX:20.0, maxY:20.5, maxZ:0.0]
lineOnPixels (square) bounding box: BoundingBox [minX:9.5, minY:29.0, minZ:0.0, width:11.0, height:1.0, depth:0.0, maxX:20.5, maxY:30.0, maxZ:0.0]
lineOnPixels (butt) bounding box: BoundingBox [minX:10.0, minY:39.0, minZ:0.0, width:10.0, height:1.0, depth:0.0, maxX:20.0, maxY:40.0, maxZ:0.0]
Related
I'm working on an audio visualisation that's basically supposed to be a circular spectrogram. I have a graph that shows the frequency already and an arc, that evolves based on the time passed. Now I would like to fill the arc with white points based on the amplitude of each frequency, much like here: https://vimeo.com/27135957. Apparently, I need to make a PGraphics that is filled with points, which change from white to black based on the amplitude. Then I need to texture the arc with this graphic. Does anyone know how to do this?
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;
Minim minim;
AudioPlayer song;
FFT fft;
PGraphics pg;
PShape arc;
float deg = 90;
float rad = radians(deg);
void setup()
{
size(1000, 1000);
minim = new Minim(this);
song = minim.loadFile("Anthology.mp3");
song.play();
fft = new FFT(song.bufferSize(), song.sampleRate());
pg = createGraphics(width, height);
}
void draw()
{
background(0);
fft.forward(song.mix);
for (int i = 0; i < fft.specSize(); i++)
{
pushMatrix();
stroke(255);
line(i, height, i, height - fft.getBand(i)*0.5);
popMatrix();
println(fft.getBand(i));
//Map Amplitude to 0 → 255, fill with points and color them
float brightness = map(fft.getBand(i), -1, 1, 0, 255);
pg.beginDraw();
pg.endDraw();
fill(255, 255, 255,);
noStroke();
float evolution = radians(map(song.position(), 0, song.length(), 90, 450));
//texture(pg);
arc(height/2, height/2, height-100, height-100, rad, evolution, PIE);
}
}
There are few concepts that appear unclear based on your code:
If you plan to render the arc within the pg PGraphics instance access pg with . notation and call drawing functions in between beginDraw()/endDraw() calls. At the moment there is nothing rendered in pg and pg isn't rendered anywhere using image(). For more details see the createGraphics() reference, run the sample code/ tweak it/ break it/fix it/understand it
Similarly PShape arc is created but is not used
There is a commented attempt to use pg as a texture however the texture mapping is unclear
If using both PGraphics and PShape is confusing you can achieve a similar effect with PGraphics alone: simply render some larger gray dots instead of arcs. It won't be an identical effect but it will have a very similar look with less effort.
Here's a variant based on your code:
import ddf.minim.*;
import ddf.minim.analysis.*;
import ddf.minim.effects.*;
import ddf.minim.signals.*;
import ddf.minim.spi.*;
import ddf.minim.ugens.*;
Minim minim;
AudioPlayer song;
FFT fft;
PGraphics pg;
void setup()
{
size(600, 600, P2D);
minim = new Minim(this);
song = minim.loadFile("jingle.mp3", 1024);
song.loop();
fft = new FFT(song.bufferSize(), song.sampleRate());
// optional: use logarithmic averages: clover to how we perceive sound
fft.logAverages( 30, 6 );
// setup pg graphics layer disable fill, make points stroke thick
pg = createGraphics(width, height);
pg.beginDraw();
pg.strokeWeight(3);
pg.noFill();
pg.endDraw();
}
void draw()
{
background(0);
image(pg, 0, 0);
// perform FFT on stereo mix
fft.forward(song.mix);
// center coordinates
float cx = width * 0.5;
float cy = height * 0.5;
// count FFT bins
int fftSpecSize = fft.specSize();
// calculate the visual size for representing an FFT bin
float sizePerSpec = (height * 0.5 ) / fftSpecSize;
stroke(255);
noFill();
// start #editing# the pg layer (once
pg.beginDraw();
// start the FFT graph shape
beginShape();
// for each FFT bin
for (int i = 0; i < fftSpecSize; i++)
{
// get the vands in reverse order (low frequencies last)
float fftBand = fft.getBand(fftSpecSize - i - 1);
// scale FFT bin value to pixel/render size
float xOffset = fftBand * 10;
// map FFT bins to 0-255 brightness levels (note 35 may differ
float brightness = map(fftBand, 0, 35, 0, 255);
// draw the line graph vertex
vertex(cx + xOffset, cy + sizePerSpec * i);
// map song position (millis played) to 360 degrees in radians (2 * PI)
// add HALF_PI (90 degrees) because 0 degrees points to the right and drawing should start pointing down (not right)
//float angle = map(song.position(), 0, song.length(), 0, TWO_PI) + HALF_PI;
// as a test map it to a lower value
float angle = (frameCount * 0.0025) + HALF_PI;
// map radius from FFT index
float radius = map(i, 0, fftSpecSize - 1, 0, width * 0.5);
// use mapped brightness as point stroke
pg.stroke(brightness);
// use polar coordinates mapped from the centre
pg.pushMatrix();
pg.translate(cx,cy);
pg.rotate(angle);
pg.point(radius,0);
pg.popMatrix();
// alternatively use polar to cartesian coordinate conversion
// x = cos(angle) * radius
// y = sin((angle) * radius
// cx, cy are added to offset from center
//pg.point(cx + (cos(angle) * radius),
// cy + (sin(angle) * radius));
}
// finish FFT graph line
endShape();
// fnish pg layer
pg.endDraw();
}
Note
you may want to change jingle.mp3 to your audio file name
for the sake of a test with a short track I used an arbitrary mapping of the angle (same as evolution in your code): there is a commented version that takes the track duration into account
the grayscale point position is rendered using coordinate transformations. Be sure to go through the 2D Transformations tutorial and bare in mind the order of transformations is important. Alternatively there is a versioon that does the same using the polar (angle/radius) to cartesian (x,y) coordinate system transformation formula instead.
P.S. I also wondered how to get nice visuals based on FFT data and with a few filtering tricks results can be nice. I recommend also checking out wakjah's answer here.
I have coordinates to align imageviews on these, programmatically. And right now images are being aligned but the image starts from this coordinate or I should say that the coordinate becomes its top left corner of my imageview and I want to make it center of my imageview. how to make coordinate center of imageview? Right now I'm doing this:
ImageView iv = new ImageView(this);
float x_coordinate = 256;
float y_coordinate = 350;
iv.setX(x_coordinate);
iv.setY(y_coordinate);
iv.setImageResource(R.drawable.myimage);
iv.setLayoutParams(new LayoutParams(
LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
mylayout.addView(iv);
I didn't find the simple one line solution but i solved my problem doing the following procedure. Here Place is my model class of which getX_Cord() and getY_Cord() returns the x and y coordinate of image, And you have to save an image in your drawable folder of which size you want to set on your screen in my case this is R.drawable.placeImage.
// display parameters
Point size = new Point();
Display display = getWindowManager().getDefaultDisplay();
display.getSize(size);
width = size.x;
height = size.y;
final Place place = roomPlace.get(i);
Drawable d = getResources().getDrawable(R.drawable.placeImage);
int y = d.getIntrinsicHeight() / 2;
int x = d.getIntrinsicWidth() / 2;
placeImage.setX((place.getX_Cord() * width) - x);
placeImage.setY((place.getY_Cord() * height) - y);
May be someone gets with more appropriate solution but i solved my problem doing this.
I'm trying to draw some rotated texts by using the CGAffineTransform.MakeRotation method at specifc location. I also make use of the TranslateCTM, but something must be wrong as rotated texts do not appear aligned and at the correct x, y position where they should appear, here is simple the code I'm using, anyone know where the problem is? :
public override void Draw (RectangleF rect)
{
DrawTextRotated("Hello1",10,100,30);
DrawTextRotated("Hello2",50,100,60);
SetNeedsDisplay();
}
static public float DegreesToRadians(float x)
{
return (float) (Math.PI * x / 180.0);
}
public void DrawTextRotated(string text,int x, int y, int rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
c.TextMatrix = CGAffineTransform.MakeRotation((float)DegreesToRadians((float)(-rotDegree)));
c.ConcatCTM(c.TextMatrix);
float xxx = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*y);
float yyy = ((float)Math.Sin(DegreesToRadians((float)rotDegree))*x);
// Move the context back into the view
c.TranslateCTM(-xxx,yyy);
c.SetTextDrawingMode(CGTextDrawingMode.Fill);
c.SetShouldSmoothFonts(true);
MonoTouch.Foundation.NSString str = new MonoTouch.Foundation.NSString(text);
SizeF strSize = new SizeF();
strSize = str.StringSize(UIFont.SystemFontOfSize(12));
RectangleF tmpR = new RectangleF(x,y,strSize.Width,strSize.Height);
str.DrawString(tmpR,UIFont.SystemFontOfSize(12),UILineBreakMode.WordWrap,UITextAlignment.Right);
c.RestoreState();
}
Thanks !
Here's some code that will draw text rotated properly about the top-left corner of the text. For the moment, I'm disregarding your use of text alignment.
First, a utility method to draw a marker where we expect the text to show up:
public void DrawMarker(float x, float y)
{
float SZ = 20;
CGContext c = UIGraphics.GetCurrentContext();
c.BeginPath();
c.AddLines( new [] { new PointF(x-SZ,y), new PointF(x+SZ,y) });
c.AddLines( new [] { new PointF(x,y-SZ), new PointF(x,y+SZ) });
c.StrokePath();
}
And the code to draw the text (note I've replaced all int rotations with float, and you may want negate your rotation):
public void DrawTextRotated(string text, float x, float y, float rotDegree)
{
CGContext c = UIGraphics.GetCurrentContext();
c.SaveState();
DrawMarker(x,y);
// Proper rotation about a point
var m = CGAffineTransform.MakeTranslation(-x,-y);
m.Multiply( CGAffineTransform.MakeRotation(DegreesToRadians(rotDegree)));
m.Multiply( CGAffineTransform.MakeTranslation(x,y));
c.ConcatCTM( m );
// Draws text UNDER the point
// "This point represents the top-left corner of the string’s bounding box."
//http://developer.apple.com/library/ios/#documentation/UIKit/Reference/NSString_UIKit_Additions/Reference/Reference.html
NSString ns = new NSString(text);
UIFont font = UIFont.SystemFontOfSize(12);
SizeF sz = ns.StringSize(font);
RectangleF rect = new RectangleF(x,y,sz.Width,sz.Height);
ns.DrawString( rect, font);
c.RestoreState();
}
Rotation about a point requires translation of the point to the origin followed by rotation, followed by rotation back to the original point. CGContext.TextMatrix has no effect on NSString.DrawString so you can just use ConcatCTM.
The alignment and line break modes don't have any effect. Since you're using NSString.StringSize, the bounding rectangle fits the entirety of the text, snug up against the left and right edges. If you make the width of the bounding rectangle wider and use UITextAlignment.Right, you'll get proper right alignment, but the text will still rotate around the top left corner of the entire bounding rectangle. Which is not, I'm guessing, what you're expecting.
If you want the text to rotate around the top right corner, let me know and I'll adjust the code accordingly.
Here's the code I used in my test:
DrawTextRotated("Hello 0",100, 50, 0);
DrawTextRotated("Hello 30",100,100,30);
DrawTextRotated("Hello 60",100,150,60);
DrawTextRotated("Hello 90",100,200,90);
Cheers.
XNA doesn't have any methods which support circle drawing.
Normally when I had to draw circle, always with the same color, I just made image with that circle and then I could display it as a sprite.
But now the color of the circle is specified during runtime, any ideas how to deal with that?
You can simply make an image of a circle with a Transparent background and the coloured part of the circle as White. Then, when it comes to drawing the circles in the Draw() method, select the tint as what you want it to be:
Texture2D circle = CreateCircle(100);
// Change Color.Red to the colour you want
spriteBatch.Draw(circle, new Vector2(30, 30), Color.Red);
Just for fun, here is the CreateCircle method:
public Texture2D CreateCircle(int radius)
{
int outerRadius = radius*2 + 2; // So circle doesn't go out of bounds
Texture2D texture = new Texture2D(GraphicsDevice, outerRadius, outerRadius);
Color[] data = new Color[outerRadius * outerRadius];
// Colour the entire texture transparent first.
for (int i = 0; i < data.Length; i++)
data[i] = Color.TransparentWhite;
// Work out the minimum step necessary using trigonometry + sine approximation.
double angleStep = 1f/radius;
for (double angle = 0; angle < Math.PI*2; angle += angleStep)
{
// Use the parametric definition of a circle: http://en.wikipedia.org/wiki/Circle#Cartesian_coordinates
int x = (int)Math.Round(radius + radius * Math.Cos(angle));
int y = (int)Math.Round(radius + radius * Math.Sin(angle));
data[y * outerRadius + x + 1] = Color.White;
}
texture.SetData(data);
return texture;
}
I am currently working on a simple Silverlight app that will allow people to upload an image, crop, resize and rotate it and then load it via a webservice to a CMS.
Cropping and resizing is done, however rotation is causing some problems. The image gets cropped and is off centre after the rotation.
WriteableBitmap wb = new WriteableBitmap(destWidth, destHeight);
RotateTransform rt = new RotateTransform();
rt.Angle = 90;
rt.CenterX = width/2;
rt.CenterY = height/2;
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = width;
tempImage2.Height = height;
tempImage2.Source = rawImage;
wb.Render(tempImage2,rt);
wb.Invalidate();
rawImage = wb;
message.Text = "h:" + rawImage.PixelHeight.ToString();
message.Text += ":w:" + rawImage.PixelWidth.ToString();
//Finally set the Image back
MyImage.Source = wb;
MyImage.Width = destWidth;
MyImage.Height = destHeight;
The code above only needs to rotate by 90° at this time so I'm just setting destWidth and destHeight to the height and width of the original image.
It looks like your target image is the same size as your source image. If you want to rotate over 90 degrees, your width and height should be exchanged:
WriteableBitmap wb = new WriteableBitmap(destHeight, destWidth);
Also, if you rotate about the centre of the original image, part of it will end up outside the boundaries. You could either include some translation transforms, or simply rotate the image about a different point:
rt.CenterX = rt.CenterY = Math.Min(width / 2, height / 2);
Try it with a piece of rectangular paper to see why that makes sense.
Many thanks to those above.. they helped a lot. I include here a simple example which includes the additional transform necessary to move the rotated image back to the top left corner of the result.
int width = currentImage.PixelWidth;
int height = currentImage.PixelHeight;
int full = Math.Max(width, height);
Image tempImage2 = new Image();
tempImage2.Width = full;
tempImage2.Height = full;
tempImage2.Source = currentImage;
// New bitmap has swapped width/height
WriteableBitmap wb1 = new WriteableBitmap(height,width);
TransformGroup transformGroup = new TransformGroup();
// Rotate around centre
RotateTransform rotate = new RotateTransform();
rotate.Angle = 90;
rotate.CenterX = full/2;
rotate.CenterY = full/2;
transformGroup.Children.Add(rotate);
// and transform back to top left corner of new image
TranslateTransform translate = new TranslateTransform();
translate.X = -(full - height) / 2;
translate.Y = -(full - width) / 2;
transformGroup.Children.Add(translate);
wb1.Render(tempImage2, transformGroup);
wb1.Invalidate();
If the image isn't square you will get cropping.
I know this won't give you exactly the right result, you'll need to crop it afterwards, but it will create a bitmap big enough in each direction to take the rotated image.
//Draw to the Writeable Bitmap
Image tempImage2 = new Image();
tempImage2.Width = Math.Max(width, height);
tempImage2.Height = Math.Max(width, height);
tempImage2.Source = rawImage;
You need to calculate the scaling based on the rotation of the corners relative to the centre.
If the image is a square only one corner is needed, but for a rectangle you need to check 2 corners in order to see if a vertical or horizontal edge is overlapped. This check is a linear comparison of how much the rectangle's height and width are exceeded.
Click here for the working testbed app created for this answer (image below):
double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
The pseudo-code is as follows (actual C# code at the end):
Convert rotation angle into Radians
Calculate the "radius" from the rectangle centre to a corner
Convert BR corner position to polar coordinates
Convert BL corner position to polar coordinates
Apply the rotation to both polar coordinates
Convert the new positions back to Cartesian coordinates (ABS value)
Find the largest of the 2 horizontal positions
Find the largest of the 2 vertical positions
Calculate the delta change for horizontal size
Calculate the delta change for vertical size
Return width/2 / x if horizontal change is greater
Return height/2 / y if vertical change is greater
The result is a multiplier that will scale the image down to fit the original rectangle regardless of rotation.
**Note: While it is possible to do much of the maths using matrix operations, there are not enough calculations to warrant that. I also thought it would make a better example from first-principles.*
C# Code:
/// <summary>
/// Calculate the scaling required to fit a rectangle into a rotation of that same rectangle
/// </summary>
/// <param name="rotation">Rotation in degrees</param>
/// <param name="pixelWidth">Width in pixels</param>
/// <param name="pixelHeight">Height in pixels</param>
/// <returns>A scaling value between 1 and 0</returns>
/// <remarks>Released to the public domain 2011 - David Johnston (HiTech Magic Ltd)</remarks>
private double CalculateConstraintScale(double rotation, int pixelWidth, int pixelHeight)
{
// Convert angle to radians for the math lib
double rotationRadians = rotation * PiDiv180;
// Centre is half the width and height
double width = pixelWidth / 2.0;
double height = pixelHeight / 2.0;
double radius = Math.Sqrt(width * width + height * height);
// Convert BR corner into polar coordinates
double angle = Math.Atan(height / width);
// Now create the matching BL corner in polar coordinates
double angle2 = Math.Atan(height / -width);
// Apply the rotation to the points
angle += rotationRadians;
angle2 += rotationRadians;
// Convert back to rectangular coordinate
double x = Math.Abs(radius * Math.Cos(angle));
double y = Math.Abs(radius * Math.Sin(angle));
double x2 = Math.Abs(radius * Math.Cos(angle2));
double y2 = Math.Abs(radius * Math.Sin(angle2));
// Find the largest extents in X & Y
x = Math.Max(x, x2);
y = Math.Max(y, y2);
// Find the largest change (pixel, not ratio)
double deltaX = x - width;
double deltaY = y - height;
// Return the ratio that will bring the largest change into the region
return (deltaX > deltaY) ? width / x : height / y;
}
Example of use:
private WriteableBitmap GenerateConstrainedBitmap(BitmapImage sourceImage, int pixelWidth, int pixelHeight, double rotation)
{
double scale = CalculateConstraintScale(rotation, pixelWidth, pixelHeight);
// Create a transform to render the image rotated and scaled
var transform = new TransformGroup();
var rt = new RotateTransform()
{
Angle = rotation,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(rt);
var st = new ScaleTransform()
{
ScaleX = scale,
ScaleY = scale,
CenterX = (pixelWidth / 2.0),
CenterY = (pixelHeight / 2.0)
};
transform.Children.Add(st);
// Resize to specified target size
var tempImage = new Image()
{
Stretch = Stretch.Fill,
Width = pixelWidth,
Height = pixelHeight,
Source = sourceImage,
};
tempImage.UpdateLayout();
// Render to a writeable bitmap
var writeableBitmap = new WriteableBitmap(pixelWidth, pixelHeight);
writeableBitmap.Render(tempImage, transform);
writeableBitmap.Invalidate();
return writeableBitmap;
}
I released a Test-bed of the code on my website so you can try it for real - click to try it
P.S. Yes this is my answer from another question, duplicated exactly, but the question does require the same answer as that one to be complete.