Resizing BufferedImage - swing

i would like to resize a BufferedImage that is draw using the Graphics drawing method with AffineTransform object.
AffineTransform at = new AffineTransform();
at.scale(1, -1);
at.translate((int) xPos - xIncr, -(int)yPos);
((Graphics2D) g).drawImage(line, at, null);
How could i resize the image, that is smaller than the area i want it to be draw in ?
Thanks for help.

Given that you're already drawing the image with an AffineTransform, why not just apply another scale to expand your image by the desired amount?
AffineTransform at = new AffineTransform();
at.scale(1, -1);
at.scale(2.5, 3.0); // replace these with more appropriate scale factors
at.translate((int) xPos - xIncr, -(int)yPos);
((Graphics2D) g).drawImage(line, at, null);

Related

Scaling up viewport

My game is 200 x 320 pixels and I want to scale this up to fit any screen. The problem is I need to scale this up in integer multiples of these dimensions so the upscaled pixels don't look uneven.
What should I put in my constructor, render() and resize() methods to achieve this?
In the constructor I now have:
camera = new OrthographicCamera();
camera.setToOrtho(false,200,320);
stage = new Stage(new ScalingViewport(Scaling.fill,200,320, camera));
In render():
camera.update();
game.batch.setProjectionMatrix(camera.combined);
And in resize():
int widthScale = width/200;
int heightScale = height/320;
int newScale = Math.max(widthScale, heightScale);
int multiplesWidth = newScale*200;
int multiplesHeight = newScale*320;
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
camera.setToOrtho(false, 200, 320);
My problem is that the content isn't centered. How and where can I center whatever is on the screen? I tried with:
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
but the screen is blank!
I think the problem is that you are trying to center the screen using the screen dimensions and not the world dimensions.
Try replacing
camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0);
with
camera.position.set(stage.getViewport().getWorldWidth()/2f, stage.getViewport().getWorldHeight()/2f, 0);
EDIT:
Okay, so here is an explanation why I proposed you set the camera to (width/2, height/2). (I will leave out the third z-Coordinate from now on as it will always be 0.)
camera.position.set() sets the center point of the camera. At (0,0) the camera is positioned right at the center of the screen, depicted by the red rectangle below. What libgdx does by default, and what I proposed offsets the camera by width/2(the blue line) and height/2(the green line) which translates to the magenta rectangle.
Side note: the reason camera.position.set(Gdx.graphics.getWidth()/2f, Gdx.graphics.getHeight()/2f, 0); didn't work is because Gdx.graphics.getX() returns the width and height of the window in pixels, not in the measurement system the Viewport uses. This offsets the camera about a meter up and to the right, where you obviously didn't draw anything.
If you want the red rectangle as camera, you can change the last argument of
stage.getViewport().update(multiplesWidth, multiplesHeight, true);
to false. Or use camera.position.set(0,0,0);

Cropping specific part of the image

I am building a windows phone 8 camera app. After a photograph has been taken i want to give the user the option of cropping specific part of the image. Like if there are some specific objects in the image, when crop option is selected, it should highlight or outline that specific part of the image, and it should be able to crop it, rather than manually cropping it.
Any specific ways to do it?.
Thanks In advance.
One way would be to create a WriteableBitmap and do a TranslateTransform of the original image into the WritableBitmap. Something like::
Image workImage = new Image { Source = originalImage, Width = originalWidth, Height = originalHeight };
WriteableBitmap writeableBitmap = new WriteableBitmap(newWidth, newHeight);
writeableBitmap.Render(temporaryImage, new TranslateTransform { X = (originalWidth – newWidth) / -2, Y = (originalHeight – newHEight) / -2 });
writeableBitmap.Invalidate();
//... or some other stream
Stream newImageStream = new MemoryStream();
//set whatever quality settings you like if 75 is no good
writeableBitmap.SaveJpeg(newImageStream, newWidth, newHeight, 0, 75);
// TODO: do something with newImageStream

Scaling tile grid

I'm working on my own tile bliting engine, this one is using hexagonal tiles - but I think it doesn't differ much from regular tiles.
I have huge x,y array of tiles and they have their x,y coordinates for rendering on canvas, I iterate only the ones that should be visible on canvas in current camera position.
So I'm stuck with scaling and cant resolve this on my own.
Here is my code for drawing tiles on canvas:
public function draw():Void{
clearCanvas(); //Clear canvas (bitmapData)
var _m:Matrix;
iterateTiles(function(_tile:HexTile):Void{ // loop every tile that is visible on screen
_m = new Matrix();
_m.translate(_tile.x + cameraPoint.x,_tile.y + cameraPoint.y);//Get pre calculated tile x,y and add camera x,y
_m.scale(matrixScale, matrixScale);
drawToCanvas(_tile,_m);//Send to draw tile on canvas using Matrix
},true);
}
This works nice and fast but only problem is it scales tiles from left top corner (like regular scale would work)
Before scale
After scale
My question is how to transform tiles to always scale from center. So if tile 10:10 is in center of screen before scaling, then it should
stay there after scaling.
Sorry, I misunderstood the question, but I think I've got it now:
// Scale the distance from the original point to the center of the canvas
var xDistance:Number = ((_tile.x + cameraPoint.x) - xCenter) * matrixScale;
var yDistance:Number = ((_tile.y + cameraPoint.y) - yCenter) * matrixScale;
// Add the distances to the center of the canvas. This is where you want the tile
// to appear.
var x:Number = xCenter + xDistance;
var y:Number = yCenter + yDistance;
// Because the coordinate is going to be scaled, you need to increase it first.
x = (1 / matrixScale) * x;
y = (1 / matrixScale) * y;
_m.translate(x, y);
I have not tested this, I've just drawn it out on graph paper. Let me know if it works.

canvas isPointInPath does not work with ctx.drawImage()

I suppose this doesn't work because canvas is drawing a bitmap of a vector (and a bitmap is not a path).
Even if it did work, the bitmap is likely always has a rectangular permitter.
Is there any way to leverage something like isPointInPath when using drawImage?
example:
The top canvas is drawn using drawImage and isPointInPath does not work.
The bottom canvas is drawn using arc and isPointInPath works.
a link to my proof
** EDIT **
I draw a circle on one canvas, and use isPointInPath to see if the mouse pointer is inside the circle (bottom canvas in my example).
I also "copy" the bottom canvas to the top canvas using drawImage. Notice that isPointInPath will not work on the top canvas (most likely due to reasons I mentioned above). Is there a work-around I can use for this that will work for ANY kind of path (or bitmap)?
A canvas context has this hidden thing called the current path. ctx.beginPath, ctx.lineTo etc create this path.
When you call ctx.stroke() or ctx.fill() the canvas strokes or fills that path.
Even after it is stroked or filled, the path is still present in the context.
This path is the only thing that isPointInPath tests.
If you want to test if something is in an image you have drawn or a rectangle that was drawn with ctx.fillRect(), that is not possible using built in methods.
Typically you'd want to use a is-point-in-rectangle function that you write yourself (or get from someone else).
If you're looking for how to do pixel-perfect (instead of just the image rectangle) hit detection for an image there are various methods of doing that discussed here: Pixel perfect 2D mouse picking with Canvas
You could try reimplementing ctx.drawImage() to always draw a box behind the image itself, like so (JSFiddle example):
ctx.customDrawImage = function(image, x, y){
this.drawImage(image, x, y);
this.rect(x, y, image.width, image.height);
}
var img1 = new Image();
img1.onload = function(){
var x = y = 0;
ctx.drawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));
x = 1.25 * img1.width;
ctx.customDrawImage(img1, x, y);
console.log(ctx.isPointInPath(x + 1, y + 1));
Note: you might get side effects like the rectangle appearing over the image, or bleeding through from behind if you are not careful.
To me, isPointInPath failed after canvas was moved. So, I used:
mouseClientX -= gCanvasElement.offsetLeft;
mouseclientY -= gCanvasElement.offsetTop;
I had some more challenges, because my canvas element could be rescaled. So first when I draw the figures, in my case arc, I save them in an array together with a name and draw them:
if (this.coInit == false)
{
let co = new TempCO ();
co.name= sensor.Name;
co.path = new Path2D();
co.path.arc(c.X, c.Y, this.radius, 0, 2 * Math.PI);
this.coWithPath.push(co);
}
let coWP = this.coWithPath.find(c=>c.name == sensor.Name);
this.ctx.fillStyle = color;
this.ctx.fill(coWP.path);
Then in the mouse event, I loop over the items and check if the click event is in a path. But I also need to rescale the mouse coordinates according to the resized canvas:
getCursorPosition(event) {
const rect = this.ctx.canvas.getBoundingClientRect();
const x = ((event.clientX - rect.left ) / rect.width) * this.canvasWidth;
const y = ((event.clientY - rect.top) / rect.height) * this.canvasHeight;
this.coWithPath.forEach(c=>{
if (this.ctx.isPointInPath(c.path, x, y))
{
console.log("arc is hit", c);
//Switch light
}
});
}
So I get the current size of the canvas and rescale the point to the original size. Now it works!
This is how the TempCO looks like:
export class TempCO
{
path : Path2D;
name : string;
}

Scaling and cropping an image with fixed dimensions

I want to display an image, and it should be transformed like this:
If for example, my original image is 200x300 pixels in size, I want it to have a width of 150, and then scale the height accordingly. Then I want to crop the image, so that the result has a dimension of 150x150 pixels.
I've tried several ways, but haven't figured out how to do it.
You can calculate the scale factor by dividing new width by old width:
var scale : Number = 150 / myImage.width;
myImage.scaleX = myImage.scaleY = scale;
To crop, either use a mask on the scaled clip, or draw to a new Bitmap:
var myBitmapData : BitmapData = new BitmapData ( 150, 150 );
// use concatenated matrix of the image to scale the new bitmap data
var matrix : Matrix = myImage.transform.concatenatedMatrix;
myBitmapData.draw ( myImage, matrix );
var myBitmap : Bitmap = new Bitmap ( myBitmapData );
addChild ( myBitmap );
Depending on how many images there are, and what you are going to do with them later, you should always test both possibilities for performance.